Add availability_template to Template Fan platform (#26511)
* Added availability_template to Template Fan platform * Added to test for invalid values in availability_template * fixed component ID in test * Made availability_template redering erorr more concise * Updated AVAILABILITY_TEMPLATE Rendering error * Moved const to package Const.py * Fix import order (pylint) * Removed 'Magic' string * Cleaned up const and compare lowercase result to 'true' * reverted _available back to boolean * Fixed tests (magic values and state checks)pull/27034/head
parent
5c5f6a21af
commit
74196eaf8b
|
@ -33,6 +33,7 @@ from homeassistant.core import callback
|
|||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers.entity import async_generate_entity_id
|
||||
from homeassistant.helpers.script import Script
|
||||
from .const import CONF_AVAILABILITY_TEMPLATE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -58,6 +59,7 @@ FAN_SCHEMA = vol.Schema(
|
|||
vol.Optional(CONF_SPEED_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_OSCILLATING_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_DIRECTION_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_AVAILABILITY_TEMPLATE): cv.template,
|
||||
vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_SET_SPEED_ACTION): cv.SCRIPT_SCHEMA,
|
||||
|
@ -86,6 +88,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||
speed_template = device_config.get(CONF_SPEED_TEMPLATE)
|
||||
oscillating_template = device_config.get(CONF_OSCILLATING_TEMPLATE)
|
||||
direction_template = device_config.get(CONF_DIRECTION_TEMPLATE)
|
||||
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
|
||||
|
||||
on_action = device_config[CONF_ON_ACTION]
|
||||
off_action = device_config[CONF_OFF_ACTION]
|
||||
|
@ -103,6 +106,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||
speed_template,
|
||||
oscillating_template,
|
||||
direction_template,
|
||||
availability_template,
|
||||
):
|
||||
if template is None:
|
||||
continue
|
||||
|
@ -131,6 +135,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||
speed_template,
|
||||
oscillating_template,
|
||||
direction_template,
|
||||
availability_template,
|
||||
on_action,
|
||||
off_action,
|
||||
set_speed_action,
|
||||
|
@ -156,6 +161,7 @@ class TemplateFan(FanEntity):
|
|||
speed_template,
|
||||
oscillating_template,
|
||||
direction_template,
|
||||
availability_template,
|
||||
on_action,
|
||||
off_action,
|
||||
set_speed_action,
|
||||
|
@ -175,6 +181,8 @@ class TemplateFan(FanEntity):
|
|||
self._speed_template = speed_template
|
||||
self._oscillating_template = oscillating_template
|
||||
self._direction_template = direction_template
|
||||
self._availability_template = availability_template
|
||||
self._available = True
|
||||
self._supported_features = 0
|
||||
|
||||
self._on_script = Script(hass, on_action)
|
||||
|
@ -207,6 +215,8 @@ class TemplateFan(FanEntity):
|
|||
if self._direction_template:
|
||||
self._direction_template.hass = self.hass
|
||||
self._supported_features |= SUPPORT_DIRECTION
|
||||
if self._availability_template:
|
||||
self._availability_template.hass = self.hass
|
||||
|
||||
self._entities = entity_ids
|
||||
# List of valid speeds
|
||||
|
@ -252,6 +262,11 @@ class TemplateFan(FanEntity):
|
|||
"""Return the polling state."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return availability of Device."""
|
||||
return self._available
|
||||
|
||||
# pylint: disable=arguments-differ
|
||||
async def async_turn_on(self, speed: str = None) -> None:
|
||||
"""Turn on the fan."""
|
||||
|
@ -422,3 +437,17 @@ class TemplateFan(FanEntity):
|
|||
", ".join(_VALID_DIRECTIONS),
|
||||
)
|
||||
self._direction = None
|
||||
|
||||
# Update Availability if 'availability_template' is defined
|
||||
if self._availability_template is not None:
|
||||
try:
|
||||
self._available = (
|
||||
self._availability_template.async_render().lower() == "true"
|
||||
)
|
||||
except (TemplateError, ValueError) as ex:
|
||||
_LOGGER.error(
|
||||
"Could not render %s template %s: %s",
|
||||
CONF_AVAILABILITY_TEMPLATE,
|
||||
self._name,
|
||||
ex,
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ import pytest
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.const import STATE_ON, STATE_OFF
|
||||
from homeassistant.const import STATE_ON, STATE_OFF, STATE_UNAVAILABLE
|
||||
from homeassistant.components.fan import (
|
||||
ATTR_SPEED,
|
||||
ATTR_OSCILLATING,
|
||||
|
@ -26,6 +26,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||
_TEST_FAN = "fan.test_fan"
|
||||
# Represent for fan's state
|
||||
_STATE_INPUT_BOOLEAN = "input_boolean.state"
|
||||
# Represent for fan's state
|
||||
_STATE_AVAILABILITY_BOOLEAN = "availability_boolean.state"
|
||||
# Represent for fan's speed
|
||||
_SPEED_INPUT_SELECT = "input_select.speed"
|
||||
# Represent for fan's oscillating
|
||||
|
@ -214,6 +216,49 @@ async def test_templates_with_entities(hass, calls):
|
|||
_verify(hass, STATE_ON, SPEED_MEDIUM, True, DIRECTION_FORWARD)
|
||||
|
||||
|
||||
async def test_availability_template_with_entities(hass, calls):
|
||||
"""Test availability tempalates with values from other entities."""
|
||||
|
||||
with assert_setup_component(1, "fan"):
|
||||
assert await setup.async_setup_component(
|
||||
hass,
|
||||
"fan",
|
||||
{
|
||||
"fan": {
|
||||
"platform": "template",
|
||||
"fans": {
|
||||
"test_fan": {
|
||||
"availability_template": "{{ is_state('availability_boolean.state', 'on') }}",
|
||||
"value_template": "{{ 'on' }}",
|
||||
"speed_template": "{{ 'medium' }}",
|
||||
"oscillating_template": "{{ 1 == 1 }}",
|
||||
"direction_template": "{{ 'forward' }}",
|
||||
"turn_on": {"service": "script.fan_on"},
|
||||
"turn_off": {"service": "script.fan_off"},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# When template returns true..
|
||||
hass.states.async_set(_STATE_AVAILABILITY_BOOLEAN, STATE_ON)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Device State should not be unavailable
|
||||
assert hass.states.get(_TEST_FAN).state != STATE_UNAVAILABLE
|
||||
|
||||
# When Availability template returns false
|
||||
hass.states.async_set(_STATE_AVAILABILITY_BOOLEAN, STATE_OFF)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# device state should be unavailable
|
||||
assert hass.states.get(_TEST_FAN).state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_templates_with_valid_values(hass, calls):
|
||||
"""Test templates with valid values."""
|
||||
with assert_setup_component(1, "fan"):
|
||||
|
@ -272,6 +317,39 @@ async def test_templates_invalid_values(hass, calls):
|
|||
_verify(hass, STATE_OFF, None, None, None)
|
||||
|
||||
|
||||
async def test_invalid_availability_template_keeps_component_available(hass, caplog):
|
||||
"""Test that an invalid availability keeps the device available."""
|
||||
|
||||
with assert_setup_component(1, "fan"):
|
||||
assert await setup.async_setup_component(
|
||||
hass,
|
||||
"fan",
|
||||
{
|
||||
"fan": {
|
||||
"platform": "template",
|
||||
"fans": {
|
||||
"test_fan": {
|
||||
"value_template": "{{ 'on' }}",
|
||||
"availability_template": "{{ x - 12 }}",
|
||||
"speed_template": "{{ states('input_select.speed') }}",
|
||||
"oscillating_template": "{{ states('input_select.osc') }}",
|
||||
"direction_template": "{{ states('input_select.direction') }}",
|
||||
"turn_on": {"service": "script.fan_on"},
|
||||
"turn_off": {"service": "script.fan_off"},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("fan.test_fan").state != STATE_UNAVAILABLE
|
||||
assert ("Could not render availability_template template") in caplog.text
|
||||
assert ("UndefinedError: 'x' is undefined") in caplog.text
|
||||
|
||||
|
||||
# End of template tests #
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue