Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions homeassistant/components/template/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
CONF_VALUE_TEMPLATE,
CONF_ICON_TEMPLATE,
CONF_ENTITY_PICTURE_TEMPLATE,
CONF_AVAILABILITY_TEMPLATE,
Comment thread
pvizeli marked this conversation as resolved.
Outdated
ATTR_ENTITY_ID,
CONF_SENSORS,
EVENT_HOMEASSISTANT_START,
Expand All @@ -39,6 +40,7 @@
vol.Optional(CONF_ICON_TEMPLATE): cv.template,
vol.Optional(CONF_ENTITY_PICTURE_TEMPLATE): cv.template,
vol.Optional(CONF_FRIENDLY_NAME_TEMPLATE): cv.template,
vol.Optional(CONF_AVAILABILITY_TEMPLATE): cv.template,
vol.Optional(CONF_ATTRIBUTE_TEMPLATES, default={}): vol.Schema(
{cv.string: cv.template}
),
Expand All @@ -62,6 +64,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
state_template = device_config[CONF_VALUE_TEMPLATE]
icon_template = device_config.get(CONF_ICON_TEMPLATE)
entity_picture_template = device_config.get(CONF_ENTITY_PICTURE_TEMPLATE)
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
friendly_name_template = device_config.get(CONF_FRIENDLY_NAME_TEMPLATE)
unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT)
Expand All @@ -77,6 +80,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
CONF_ICON_TEMPLATE: icon_template,
CONF_ENTITY_PICTURE_TEMPLATE: entity_picture_template,
CONF_FRIENDLY_NAME_TEMPLATE: friendly_name_template,
CONF_AVAILABILITY_TEMPLATE: availability_template,
}

for tpl_name, template in chain(templates.items(), attribute_templates.items()):
Expand Down Expand Up @@ -120,6 +124,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
state_template,
icon_template,
entity_picture_template,
availability_template,
entity_ids,
device_class,
attribute_templates,
Expand All @@ -146,6 +151,7 @@ def __init__(
state_template,
icon_template,
entity_picture_template,
availability_template,
entity_ids,
device_class,
attribute_templates,
Expand All @@ -162,10 +168,12 @@ def __init__(
self._state = None
self._icon_template = icon_template
self._entity_picture_template = entity_picture_template
self._availability_template = availability_template
self._icon = None
self._entity_picture = None
self._entities = entity_ids
self._device_class = device_class
self._available = True
self._attribute_templates = attribute_templates
self._attributes = {}

Expand Down Expand Up @@ -222,6 +230,11 @@ def unit_of_measurement(self):
"""Return the unit_of_measurement of the device."""
return self._unit_of_measurement

@property
def available(self) -> bool:
"""Return if the device is available."""
return self._available

@property
def device_state_attributes(self):
"""Return the state attributes."""
Expand Down Expand Up @@ -291,3 +304,10 @@ async def async_update(self):
self._name,
ex,
)
if self._availability_template is not None:
Comment thread
grillp marked this conversation as resolved.
Outdated
try:
result = self._availability_template.async_render()
self._available = result == "true"
except (TemplateError, ValueError) as err:
_LOGGER.error(err)
Comment thread
grillp marked this conversation as resolved.
Outdated
self._available = True
1 change: 1 addition & 0 deletions homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
CONF_AUTHENTICATION = "authentication"
CONF_AUTH_MFA_MODULES = "auth_mfa_modules"
CONF_AUTH_PROVIDERS = "auth_providers"
CONF_AVAILABILITY_TEMPLATE = "availability_template"
Comment thread
grillp marked this conversation as resolved.
Outdated
CONF_BASE = "base"
CONF_BEFORE = "before"
CONF_BELOW = "below"
Expand Down
73 changes: 73 additions & 0 deletions tests/components/template/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from homeassistant.setup import setup_component, async_setup_component

from tests.common import get_test_home_assistant, assert_setup_component
from homeassistant.const import STATE_UNAVAILABLE


class TestTemplateSensor:
Expand Down Expand Up @@ -376,6 +377,52 @@ def test_setup_valid_device_class(self):
state = self.hass.states.get("sensor.test2")
assert "device_class" not in state.attributes

def test_available_template_with_entities(self):
Comment thread
grillp marked this conversation as resolved.
Outdated
"""Test availability tempalates with values from other entities."""
availability_template = """
{% if is_state('availability_boolean.state', 'True') %}
{{ 'true' }}
{% else %}
{{ 'false' }}
{% endif %}
"""

with assert_setup_component(1):
assert setup_component(
self.hass,
"sensor",
{
"sensor": {
"platform": "template",
"sensors": {
"test_template_sensor": {
"value_template": "{{ states.sensor.test_state.state }}",
"availability_template": availability_template,
}
},
}
},
)

self.hass.start()
self.hass.block_till_done()

# When template returns true..
self.hass.states.set("availability_boolean.state", True)
Comment thread
grillp marked this conversation as resolved.
Outdated
self.hass.block_till_done()

# Device State should not be unavailable
state = self.hass.states.get("sensor.test_template_sensor")
assert state.state != STATE_UNAVAILABLE

# When Availability template returns false
self.hass.states.set("availability_boolean.state", False)
self.hass.block_till_done()

# device state should be unavailable
state = self.hass.states.get("sensor.test_template_sensor")
assert state.state == STATE_UNAVAILABLE


async def test_invalid_attribute_template(hass, caplog):
"""Test that errors are logged if rendering template fails."""
Expand Down Expand Up @@ -405,6 +452,32 @@ async def test_invalid_attribute_template(hass, caplog):
assert ("Error rendering attribute test_attribute") in caplog.text


async def test_invalid_availability_template_keeps_component_available(hass, caplog):
"""Test that an invalid availability keeps the device available."""

await async_setup_component(
hass,
"sensor",
{
"sensor": {
"platform": "template",
"sensors": {
"my_sensor": {
"value_template": "{{ states.sensor.test_state.state }}",
"availability_template": "{{ x - 12 }}",
}
},
}
},
)

await hass.async_start()
Comment thread
grillp marked this conversation as resolved.
await hass.async_block_till_done()

assert hass.states.get("sensor.my_sensor") != STATE_UNAVAILABLE
Comment thread
grillp marked this conversation as resolved.
Outdated
assert ("UndefinedError: 'x' is undefined") in caplog.text


async def test_no_template_match_all(hass, caplog):
"""Test that we do not allow sensors that match on all."""
hass.states.async_set("sensor.test_sensor", "startup")
Expand Down