Add availability_template to Template Vacuum platform (#26514)

* Added availability_template to Template Vacuum platform

* Added to test for invalid values in availability_template

* Updated AVAILABILITY_TEMPLATE Rendering error

* Moved const to package Const.py

* Removed 'Magic' string

* Cleaned up const and compare lowercase result to 'true'

* reverted _available back to boolean

* Fixed tests (async, magic values and state checks)
pull/27034/head
Gil Peeters 2019-09-28 22:02:46 +10:00 committed by Charles Garwood
parent ed82ec5d8e
commit 11c9bab078
2 changed files with 90 additions and 1 deletions

View File

@ -44,6 +44,8 @@ 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__)
CONF_VACUUMS = "vacuums"
@ -67,6 +69,7 @@ VACUUM_SCHEMA = vol.Schema(
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_BATTERY_LEVEL_TEMPLATE): cv.template,
vol.Optional(CONF_FAN_SPEED_TEMPLATE): cv.template,
vol.Optional(CONF_AVAILABILITY_TEMPLATE): cv.template,
vol.Required(SERVICE_START): cv.SCRIPT_SCHEMA,
vol.Optional(SERVICE_PAUSE): cv.SCRIPT_SCHEMA,
vol.Optional(SERVICE_STOP): cv.SCRIPT_SCHEMA,
@ -94,6 +97,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
state_template = device_config.get(CONF_VALUE_TEMPLATE)
battery_level_template = device_config.get(CONF_BATTERY_LEVEL_TEMPLATE)
fan_speed_template = device_config.get(CONF_FAN_SPEED_TEMPLATE)
availability_template = device_config.get(CONF_AVAILABILITY_TEMPLATE)
start_action = device_config[SERVICE_START]
pause_action = device_config.get(SERVICE_PAUSE)
@ -113,6 +117,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
(CONF_VALUE_TEMPLATE, state_template),
(CONF_BATTERY_LEVEL_TEMPLATE, battery_level_template),
(CONF_FAN_SPEED_TEMPLATE, fan_speed_template),
(CONF_AVAILABILITY_TEMPLATE, availability_template),
):
if template is None:
continue
@ -152,6 +157,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
state_template,
battery_level_template,
fan_speed_template,
availability_template,
start_action,
pause_action,
stop_action,
@ -178,6 +184,7 @@ class TemplateVacuum(StateVacuumDevice):
state_template,
battery_level_template,
fan_speed_template,
availability_template,
start_action,
pause_action,
stop_action,
@ -198,6 +205,7 @@ class TemplateVacuum(StateVacuumDevice):
self._template = state_template
self._battery_level_template = battery_level_template
self._fan_speed_template = fan_speed_template
self._availability_template = availability_template
self._supported_features = SUPPORT_START
self._start_script = Script(hass, start_action)
@ -235,6 +243,7 @@ class TemplateVacuum(StateVacuumDevice):
self._state = None
self._battery_level = None
self._fan_speed = None
self._available = True
if self._template:
self._supported_features |= SUPPORT_STATE
@ -280,6 +289,11 @@ class TemplateVacuum(StateVacuumDevice):
"""Return the polling state."""
return False
@property
def available(self) -> bool:
"""Return if the device is available."""
return self._available
async def async_start(self):
"""Start or resume the cleaning task."""
await self._start_script.async_run(context=self._context)
@ -421,3 +435,16 @@ class TemplateVacuum(StateVacuumDevice):
self._fan_speed_list,
)
self._fan_speed = 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,
)

View File

@ -3,7 +3,7 @@ import logging
import pytest
from homeassistant import setup
from homeassistant.const import STATE_ON, STATE_UNKNOWN
from homeassistant.const import STATE_ON, STATE_OFF, STATE_UNKNOWN, STATE_UNAVAILABLE
from homeassistant.components.vacuum import (
ATTR_BATTERY_LEVEL,
STATE_CLEANING,
@ -210,6 +210,68 @@ async def test_invalid_templates(hass, calls):
_verify(hass, STATE_UNKNOWN, None)
async def test_available_template_with_entities(hass, calls):
"""Test availability templates with values from other entities."""
assert await setup.async_setup_component(
hass,
"vacuum",
{
"vacuum": {
"platform": "template",
"vacuums": {
"test_template_vacuum": {
"availability_template": "{{ is_state('availability_state.state', 'on') }}",
"start": {"service": "script.vacuum_start"},
}
},
}
},
)
await hass.async_start()
await hass.async_block_till_done()
# When template returns true..
hass.states.async_set("availability_state.state", STATE_ON)
await hass.async_block_till_done()
# Device State should not be unavailable
assert hass.states.get("vacuum.test_template_vacuum").state != STATE_UNAVAILABLE
# When Availability template returns false
hass.states.async_set("availability_state.state", STATE_OFF)
await hass.async_block_till_done()
# device state should be unavailable
assert hass.states.get("vacuum.test_template_vacuum").state == STATE_UNAVAILABLE
async def test_invalid_availability_template_keeps_component_available(hass, caplog):
"""Test that an invalid availability keeps the device available."""
assert await setup.async_setup_component(
hass,
"vacuum",
{
"vacuum": {
"platform": "template",
"vacuums": {
"test_template_vacuum": {
"availability_template": "{{ x - 12 }}",
"start": {"service": "script.vacuum_start"},
}
},
}
},
)
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("vacuum.test_template_vacuum") != STATE_UNAVAILABLE
assert ("UndefinedError: 'x' is undefined") in caplog.text
# End of template tests #