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."""
|
"""Support for exposing a templated binary sensor."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
DEVICE_CLASSES_SCHEMA,
|
DEVICE_CLASSES_SCHEMA,
|
||||||
|
DOMAIN as BINARY_SENSOR_DOMAIN,
|
||||||
ENTITY_ID_FORMAT,
|
ENTITY_ID_FORMAT,
|
||||||
PLATFORM_SCHEMA,
|
PLATFORM_SCHEMA,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.template import TriggerUpdateCoordinator
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
|
@ -40,6 +45,7 @@ from .const import (
|
||||||
CONF_PICTURE,
|
CONF_PICTURE,
|
||||||
)
|
)
|
||||||
from .template_entity import TemplateEntity
|
from .template_entity import TemplateEntity
|
||||||
|
from .trigger_entity import TriggerEntity
|
||||||
|
|
||||||
CONF_DELAY_ON = "delay_on"
|
CONF_DELAY_ON = "delay_on"
|
||||||
CONF_DELAY_OFF = "delay_off"
|
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):
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Set up the template binary sensors."""
|
"""Set up the template binary sensors."""
|
||||||
|
if discovery_info is None:
|
||||||
_async_create_template_tracking_entities(
|
_async_create_template_tracking_entities(
|
||||||
async_add_entities, hass, rewrite_legacy_to_modern_conf(config[CONF_SENSORS])
|
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, discovery_info["entities"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -283,7 +304,7 @@ class BinarySensorTemplate(TemplateEntity, BinarySensorEntity):
|
||||||
self._state = state
|
self._state = state
|
||||||
self.async_write_ha_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.
|
# state with delay. Cancelled if template result changes.
|
||||||
self._delay_cancel = async_call_later(self.hass, delay, _set_state)
|
self._delay_cancel = async_call_later(self.hass, delay, _set_state)
|
||||||
|
|
||||||
|
@ -306,3 +327,83 @@ class BinarySensorTemplate(TemplateEntity, BinarySensorEntity):
|
||||||
def device_class(self):
|
def device_class(self):
|
||||||
"""Return the sensor class of the binary sensor."""
|
"""Return the sensor class of the binary sensor."""
|
||||||
return self._device_class
|
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
|
||||||
|
)
|
||||||
|
|
|
@ -3,13 +3,14 @@ import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.config import async_log_exception, config_without_domain
|
from homeassistant.config import async_log_exception, config_without_domain
|
||||||
from homeassistant.const import CONF_SENSORS, CONF_UNIQUE_ID
|
from homeassistant.const import CONF_BINARY_SENSORS, CONF_SENSORS, CONF_UNIQUE_ID
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.trigger import async_validate_trigger_config
|
from homeassistant.helpers.trigger import async_validate_trigger_config
|
||||||
|
|
||||||
from . import sensor as sensor_platform
|
from . import binary_sensor as binary_sensor_platform, sensor as sensor_platform
|
||||||
from .const import CONF_TRIGGER, DOMAIN
|
from .const import CONF_TRIGGER, DOMAIN
|
||||||
|
|
||||||
CONFIG_SECTION_SCHEMA = vol.Schema(
|
CONFIG_SECTION_SCHEMA = vol.Schema(
|
||||||
|
@ -22,6 +23,12 @@ CONFIG_SECTION_SCHEMA = vol.Schema(
|
||||||
vol.Optional(CONF_SENSORS): cv.schema_with_slug_keys(
|
vol.Optional(CONF_SENSORS): cv.schema_with_slug_keys(
|
||||||
sensor_platform.LEGACY_SENSOR_SCHEMA
|
sensor_platform.LEGACY_SENSOR_SCHEMA
|
||||||
),
|
),
|
||||||
|
vol.Optional(BINARY_SENSOR_DOMAIN): vol.All(
|
||||||
|
cv.ensure_list, [binary_sensor_platform.BINARY_SENSOR_SCHEMA]
|
||||||
|
),
|
||||||
|
vol.Optional(CONF_BINARY_SENSORS): cv.schema_with_slug_keys(
|
||||||
|
binary_sensor_platform.LEGACY_BINARY_SENSOR_SCHEMA
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,17 +52,34 @@ async def async_validate_config(hass, config):
|
||||||
async_log_exception(err, DOMAIN, cfg, hass)
|
async_log_exception(err, DOMAIN, cfg, hass)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if CONF_SENSORS in cfg:
|
legacy_warn_printed = False
|
||||||
|
|
||||||
|
for old_key, new_key, transform in (
|
||||||
|
(
|
||||||
|
CONF_SENSORS,
|
||||||
|
SENSOR_DOMAIN,
|
||||||
|
sensor_platform.rewrite_legacy_to_modern_conf,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
CONF_BINARY_SENSORS,
|
||||||
|
BINARY_SENSOR_DOMAIN,
|
||||||
|
binary_sensor_platform.rewrite_legacy_to_modern_conf,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
if old_key not in cfg:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not legacy_warn_printed:
|
||||||
|
legacy_warn_printed = True
|
||||||
logging.getLogger(__name__).warning(
|
logging.getLogger(__name__).warning(
|
||||||
"The entity definition format under template: differs from the platform "
|
"The entity definition format under template: differs from the platform "
|
||||||
"configuration format. See "
|
"configuration format. See "
|
||||||
"https://www.home-assistant.io/integrations/template#configuration-for-trigger-based-template-sensors"
|
"https://www.home-assistant.io/integrations/template#configuration-for-trigger-based-template-sensors"
|
||||||
)
|
)
|
||||||
sensors = list(cfg[SENSOR_DOMAIN]) if SENSOR_DOMAIN in cfg else []
|
|
||||||
sensors.extend(
|
definitions = list(cfg[new_key]) if new_key in cfg else []
|
||||||
sensor_platform.rewrite_legacy_to_modern_conf(cfg[CONF_SENSORS])
|
definitions.extend(transform(cfg[old_key]))
|
||||||
)
|
cfg = {**cfg, new_key: definitions}
|
||||||
cfg = {**cfg, "sensor": sensors}
|
|
||||||
|
|
||||||
config_sections.append(cfg)
|
config_sections.append(cfg)
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ class TriggerEntity(update_coordinator.CoordinatorEntity):
|
||||||
|
|
||||||
# We make a copy so our initial render is 'unknown' and not 'unavailable'
|
# We make a copy so our initial render is 'unknown' and not 'unavailable'
|
||||||
self._rendered = dict(self._static_rendered)
|
self._rendered = dict(self._static_rendered)
|
||||||
|
self._parse_result = set()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -115,17 +116,18 @@ class TriggerEntity(update_coordinator.CoordinatorEntity):
|
||||||
template.attach(self.hass, self._config)
|
template.attach(self.hass, self._config)
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
if self.coordinator.data is not None:
|
if self.coordinator.data is not None:
|
||||||
self._handle_coordinator_update()
|
self._process_data()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _process_data(self) -> None:
|
||||||
"""Handle updated data from the coordinator."""
|
"""Process new data."""
|
||||||
try:
|
try:
|
||||||
rendered = dict(self._static_rendered)
|
rendered = dict(self._static_rendered)
|
||||||
|
|
||||||
for key in self._to_render:
|
for key in self._to_render:
|
||||||
rendered[key] = self._config[key].async_render(
|
rendered[key] = self._config[key].async_render(
|
||||||
self.coordinator.data["run_variables"], parse_result=False
|
self.coordinator.data["run_variables"],
|
||||||
|
parse_result=key in self._parse_result,
|
||||||
)
|
)
|
||||||
|
|
||||||
if CONF_ATTRIBUTES in self._config:
|
if CONF_ATTRIBUTES in self._config:
|
||||||
|
@ -142,4 +144,9 @@ class TriggerEntity(update_coordinator.CoordinatorEntity):
|
||||||
self._rendered = self._static_rendered
|
self._rendered = self._static_rendered
|
||||||
|
|
||||||
self.async_set_context(self.coordinator.data["context"])
|
self.async_set_context(self.coordinator.data["context"])
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _handle_coordinator_update(self) -> None:
|
||||||
|
"""Handle updated data from the coordinator."""
|
||||||
|
self._process_data()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -858,6 +858,9 @@ def result_as_boolean(template_result: str | None) -> bool:
|
||||||
False/0/None/'0'/'false'/'no'/'off'/'disable' are considered falsy
|
False/0/None/'0'/'false'/'no'/'off'/'disable' are considered falsy
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if template_result is None:
|
||||||
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Import here, not at top-level to avoid circular import
|
# Import here, not at top-level to avoid circular import
|
||||||
from homeassistant.helpers import ( # pylint: disable=import-outside-toplevel
|
from homeassistant.helpers import ( # pylint: disable=import-outside-toplevel
|
||||||
|
|
|
@ -12,13 +12,14 @@ from homeassistant.const import (
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import CoreState
|
from homeassistant.core import Context, CoreState
|
||||||
|
from homeassistant.helpers import entity_registry
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
async def test_setup(hass):
|
async def test_setup_legacy(hass):
|
||||||
"""Test the setup."""
|
"""Test the setup."""
|
||||||
config = {
|
config = {
|
||||||
"binary_sensor": {
|
"binary_sensor": {
|
||||||
|
@ -906,3 +907,128 @@ async def test_template_validation_error(hass, caplog):
|
||||||
|
|
||||||
state = hass.states.get("binary_sensor.test")
|
state = hass.states.get("binary_sensor.test")
|
||||||
assert state.attributes.get("icon") is None
|
assert state.attributes.get("icon") is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_trigger_entity(hass):
|
||||||
|
"""Test trigger entity works."""
|
||||||
|
assert await setup.async_setup_component(
|
||||||
|
hass,
|
||||||
|
"template",
|
||||||
|
{
|
||||||
|
"template": [
|
||||||
|
{"invalid": "config"},
|
||||||
|
# Config after invalid should still be set up
|
||||||
|
{
|
||||||
|
"unique_id": "listening-test-event",
|
||||||
|
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||||
|
"binary_sensors": {
|
||||||
|
"hello": {
|
||||||
|
"friendly_name": "Hello Name",
|
||||||
|
"unique_id": "hello_name-id",
|
||||||
|
"device_class": "battery",
|
||||||
|
"value_template": "{{ trigger.event.data.beer == 2 }}",
|
||||||
|
"entity_picture_template": "{{ '/local/dogs.png' }}",
|
||||||
|
"icon_template": "{{ 'mdi:pirate' }}",
|
||||||
|
"attribute_templates": {
|
||||||
|
"plus_one": "{{ trigger.event.data.beer + 1 }}"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"binary_sensor": [
|
||||||
|
{
|
||||||
|
"name": "via list",
|
||||||
|
"unique_id": "via_list-id",
|
||||||
|
"device_class": "battery",
|
||||||
|
"state": "{{ trigger.event.data.beer == 2 }}",
|
||||||
|
"picture": "{{ '/local/dogs.png' }}",
|
||||||
|
"icon": "{{ 'mdi:pirate' }}",
|
||||||
|
"attributes": {
|
||||||
|
"plus_one": "{{ trigger.event.data.beer + 1 }}"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": [],
|
||||||
|
"binary_sensors": {
|
||||||
|
"bare_minimum": {
|
||||||
|
"value_template": "{{ trigger.event.data.beer == 1 }}"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("binary_sensor.hello_name")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "off"
|
||||||
|
|
||||||
|
state = hass.states.get("binary_sensor.bare_minimum")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "off"
|
||||||
|
|
||||||
|
context = Context()
|
||||||
|
hass.bus.async_fire("test_event", {"beer": 2}, context=context)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("binary_sensor.hello_name")
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes.get("device_class") == "battery"
|
||||||
|
assert state.attributes.get("icon") == "mdi:pirate"
|
||||||
|
assert state.attributes.get("entity_picture") == "/local/dogs.png"
|
||||||
|
assert state.attributes.get("plus_one") == 3
|
||||||
|
assert state.context is context
|
||||||
|
|
||||||
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
assert len(ent_reg.entities) == 2
|
||||||
|
assert (
|
||||||
|
ent_reg.entities["binary_sensor.hello_name"].unique_id
|
||||||
|
== "listening-test-event-hello_name-id"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
ent_reg.entities["binary_sensor.via_list"].unique_id
|
||||||
|
== "listening-test-event-via_list-id"
|
||||||
|
)
|
||||||
|
|
||||||
|
state = hass.states.get("binary_sensor.via_list")
|
||||||
|
assert state.state == "on"
|
||||||
|
assert state.attributes.get("device_class") == "battery"
|
||||||
|
assert state.attributes.get("icon") == "mdi:pirate"
|
||||||
|
assert state.attributes.get("entity_picture") == "/local/dogs.png"
|
||||||
|
assert state.attributes.get("plus_one") == 3
|
||||||
|
assert state.context is context
|
||||||
|
|
||||||
|
|
||||||
|
async def test_template_with_trigger_templated_delay_on(hass):
|
||||||
|
"""Test binary sensor template with template delay on."""
|
||||||
|
config = {
|
||||||
|
"template": {
|
||||||
|
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||||
|
"binary_sensor": {
|
||||||
|
"name": "test",
|
||||||
|
"state": "{{ trigger.event.data.beer == 2 }}",
|
||||||
|
"device_class": "motion",
|
||||||
|
"delay_on": '{{ ({ "seconds": 6 / 2 }) }}',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await setup.async_setup_component(hass, "template", config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
|
||||||
|
state = hass.states.get("binary_sensor.test")
|
||||||
|
assert state.state == "off"
|
||||||
|
|
||||||
|
context = Context()
|
||||||
|
hass.bus.async_fire("test_event", {"beer": 2}, context=context)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
future = dt_util.utcnow() + timedelta(seconds=3)
|
||||||
|
async_fire_time_changed(hass, future)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("binary_sensor.test")
|
||||||
|
assert state.state == "on"
|
||||||
|
|
|
@ -41,6 +41,10 @@ async def test_reloadable(hass):
|
||||||
"name": "top level state",
|
"name": "top level state",
|
||||||
"state": "{{ states.sensor.top_level.state }} + 2",
|
"state": "{{ states.sensor.top_level.state }} + 2",
|
||||||
},
|
},
|
||||||
|
"binary_sensor": {
|
||||||
|
"name": "top level state",
|
||||||
|
"state": "{{ states.sensor.top_level.state == 'init' }}",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -50,15 +54,17 @@ async def test_reloadable(hass):
|
||||||
await hass.async_start()
|
await hass.async_start()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass.states.get("sensor.top_level_state").state == "unknown + 2"
|
assert hass.states.get("sensor.top_level_state").state == "unknown + 2"
|
||||||
|
assert hass.states.get("binary_sensor.top_level_state").state == "off"
|
||||||
|
|
||||||
hass.bus.async_fire("event_1", {"source": "init"})
|
hass.bus.async_fire("event_1", {"source": "init"})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 4
|
assert len(hass.states.async_all()) == 5
|
||||||
assert hass.states.get("sensor.state").state == "mytest"
|
assert hass.states.get("sensor.state").state == "mytest"
|
||||||
assert hass.states.get("sensor.top_level").state == "init"
|
assert hass.states.get("sensor.top_level").state == "init"
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass.states.get("sensor.top_level_state").state == "init + 2"
|
assert hass.states.get("sensor.top_level_state").state == "init + 2"
|
||||||
|
assert hass.states.get("binary_sensor.top_level_state").state == "on"
|
||||||
|
|
||||||
yaml_path = path.join(
|
yaml_path = path.join(
|
||||||
_get_fixtures_base_path(),
|
_get_fixtures_base_path(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue