mirror of
https://github.com/home-assistant/core.git
synced 2025-08-02 20:25:07 +02:00
Implement templates for covers (#8100)
* Implement templates for covers * Fix a few remaining pylint warnings * Fix hound line-length warnings * Fix one more hound line-length warning * Fix quadruple-quotes an line length code-quality issues * Irrelevant change to retrigger travis due to timeout * Use volutuous Exclusive to check for mutex condition * Fix incorrect state check
This commit is contained in:
committed by
Paulus Schoutsen
parent
a663dbada0
commit
17237e9d3f
354
homeassistant/components/cover/template.py
Normal file
354
homeassistant/components/cover/template.py
Normal file
@@ -0,0 +1,354 @@
|
||||
"""
|
||||
Support for covers which integrate with other components.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/cover.template/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.components.cover import (
|
||||
ENTITY_ID_FORMAT, CoverDevice, PLATFORM_SCHEMA,
|
||||
SUPPORT_OPEN_TILT, SUPPORT_CLOSE_TILT, SUPPORT_STOP_TILT,
|
||||
SUPPORT_SET_TILT_POSITION, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_STOP,
|
||||
SUPPORT_SET_POSITION, ATTR_POSITION, ATTR_TILT_POSITION)
|
||||
from homeassistant.const import (
|
||||
CONF_FRIENDLY_NAME, CONF_ENTITY_ID,
|
||||
EVENT_HOMEASSISTANT_START, MATCH_ALL,
|
||||
CONF_VALUE_TEMPLATE, CONF_ICON_TEMPLATE,
|
||||
STATE_OPEN, STATE_CLOSED)
|
||||
from homeassistant.exceptions import TemplateError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import async_generate_entity_id
|
||||
from homeassistant.helpers.event import async_track_state_change
|
||||
from homeassistant.helpers.restore_state import async_get_last_state
|
||||
from homeassistant.helpers.script import Script
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_VALID_STATES = [STATE_OPEN, STATE_CLOSED, 'true', 'false']
|
||||
|
||||
CONF_COVERS = 'covers'
|
||||
|
||||
CONF_POSITION_TEMPLATE = 'position_template'
|
||||
CONF_TILT_TEMPLATE = 'tilt_template'
|
||||
OPEN_ACTION = 'open_cover'
|
||||
CLOSE_ACTION = 'close_cover'
|
||||
STOP_ACTION = 'stop_cover'
|
||||
POSITION_ACTION = 'set_cover_position'
|
||||
TILT_ACTION = 'set_cover_tilt_position'
|
||||
CONF_VALUE_OR_POSITION_TEMPLATE = 'value_or_position'
|
||||
|
||||
TILT_FEATURES = (SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT | SUPPORT_STOP_TILT |
|
||||
SUPPORT_SET_TILT_POSITION)
|
||||
|
||||
COVER_SCHEMA = vol.Schema({
|
||||
vol.Required(OPEN_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Required(CLOSE_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Required(STOP_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Exclusive(CONF_POSITION_TEMPLATE,
|
||||
CONF_VALUE_OR_POSITION_TEMPLATE): cv.template,
|
||||
vol.Exclusive(CONF_VALUE_TEMPLATE,
|
||||
CONF_VALUE_OR_POSITION_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_POSITION_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_TILT_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_ICON_TEMPLATE): cv.template,
|
||||
vol.Optional(POSITION_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(TILT_ACTION): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_FRIENDLY_NAME, default=None): cv.string,
|
||||
vol.Optional(CONF_ENTITY_ID): cv.entity_ids
|
||||
})
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_COVERS): vol.Schema({cv.slug: COVER_SCHEMA}),
|
||||
})
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Set up the Template cover."""
|
||||
covers = []
|
||||
|
||||
for device, device_config in config[CONF_COVERS].items():
|
||||
friendly_name = device_config.get(CONF_FRIENDLY_NAME, device)
|
||||
state_template = device_config.get(CONF_VALUE_TEMPLATE)
|
||||
position_template = device_config.get(CONF_POSITION_TEMPLATE)
|
||||
tilt_template = device_config.get(CONF_TILT_TEMPLATE)
|
||||
icon_template = device_config.get(CONF_ICON_TEMPLATE)
|
||||
open_action = device_config[OPEN_ACTION]
|
||||
close_action = device_config[CLOSE_ACTION]
|
||||
stop_action = device_config[STOP_ACTION]
|
||||
position_action = device_config.get(POSITION_ACTION)
|
||||
tilt_action = device_config.get(TILT_ACTION)
|
||||
|
||||
if position_template is None and state_template is None:
|
||||
_LOGGER.error('Must specify either %s' or '%s',
|
||||
CONF_VALUE_TEMPLATE, CONF_VALUE_TEMPLATE)
|
||||
continue
|
||||
|
||||
template_entity_ids = set()
|
||||
if state_template is not None:
|
||||
temp_ids = state_template.extract_entities()
|
||||
if str(temp_ids) != MATCH_ALL:
|
||||
template_entity_ids |= set(temp_ids)
|
||||
|
||||
if position_template is not None:
|
||||
temp_ids = position_template.extract_entities()
|
||||
if str(temp_ids) != MATCH_ALL:
|
||||
template_entity_ids |= set(temp_ids)
|
||||
|
||||
if tilt_template is not None:
|
||||
temp_ids = tilt_template.extract_entities()
|
||||
if str(temp_ids) != MATCH_ALL:
|
||||
template_entity_ids |= set(temp_ids)
|
||||
|
||||
if icon_template is not None:
|
||||
temp_ids = icon_template.extract_entities()
|
||||
if str(temp_ids) != MATCH_ALL:
|
||||
template_entity_ids |= set(temp_ids)
|
||||
|
||||
if not template_entity_ids:
|
||||
template_entity_ids = MATCH_ALL
|
||||
|
||||
entity_ids = device_config.get(CONF_ENTITY_ID, template_entity_ids)
|
||||
|
||||
covers.append(
|
||||
CoverTemplate(
|
||||
hass,
|
||||
device, friendly_name, state_template,
|
||||
position_template, tilt_template, icon_template,
|
||||
open_action, close_action, stop_action,
|
||||
position_action, tilt_action, entity_ids
|
||||
)
|
||||
)
|
||||
if not covers:
|
||||
_LOGGER.error("No covers added")
|
||||
return False
|
||||
|
||||
async_add_devices(covers, True)
|
||||
return True
|
||||
|
||||
|
||||
class CoverTemplate(CoverDevice):
|
||||
"""Representation of a Template cover."""
|
||||
|
||||
def __init__(self, hass, device_id, friendly_name, state_template,
|
||||
position_template, tilt_template, icon_template,
|
||||
open_action, close_action, stop_action,
|
||||
position_action, tilt_action, entity_ids):
|
||||
"""Initialize the Template cover."""
|
||||
self.hass = hass
|
||||
self.entity_id = async_generate_entity_id(
|
||||
ENTITY_ID_FORMAT, device_id, hass=hass)
|
||||
self._name = friendly_name
|
||||
self._template = state_template
|
||||
self._position_template = position_template
|
||||
self._tilt_template = tilt_template
|
||||
self._icon_template = icon_template
|
||||
self._open_script = Script(hass, open_action)
|
||||
self._close_script = Script(hass, close_action)
|
||||
self._stop_script = Script(hass, stop_action)
|
||||
self._position_script = None
|
||||
if position_action is not None:
|
||||
self._position_script = Script(hass, position_action)
|
||||
self._tilt_script = None
|
||||
if tilt_action is not None:
|
||||
self._tilt_script = Script(hass, tilt_action)
|
||||
self._icon = None
|
||||
self._position = None
|
||||
self._tilt_value = None
|
||||
self._entities = entity_ids
|
||||
|
||||
if self._template is not None:
|
||||
self._template.hass = self.hass
|
||||
if self._position_template is not None:
|
||||
self._position_template.hass = self.hass
|
||||
if self._tilt_template is not None:
|
||||
self._tilt_template.hass = self.hass
|
||||
if self._icon_template is not None:
|
||||
self._icon_template.hass = self.hass
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
state = yield from async_get_last_state(self.hass, self.entity_id)
|
||||
if state:
|
||||
self._position = 100 if state.state == STATE_OPEN else 0
|
||||
|
||||
@callback
|
||||
def template_cover_state_listener(entity, old_state, new_state):
|
||||
"""Handle target device state changes."""
|
||||
self.hass.async_add_job(self.async_update_ha_state(True))
|
||||
|
||||
@callback
|
||||
def template_cover_startup(event):
|
||||
"""Update template on startup."""
|
||||
async_track_state_change(
|
||||
self.hass, self._entities, template_cover_state_listener)
|
||||
|
||||
self.hass.async_add_job(self.async_update_ha_state(True))
|
||||
|
||||
self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_START, template_cover_startup)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the cover."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
"""Return if the cover is closed."""
|
||||
return self._position == 0
|
||||
|
||||
@property
|
||||
def current_cover_position(self):
|
||||
"""Return current position of cover.
|
||||
|
||||
None is unknown, 0 is closed, 100 is fully open.
|
||||
"""
|
||||
return self._position
|
||||
|
||||
@property
|
||||
def current_cover_tilt_position(self):
|
||||
"""Return current position of cover tilt.
|
||||
|
||||
None is unknown, 0 is closed, 100 is fully open.
|
||||
"""
|
||||
return self._tilt_value
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon to use in the frontend, if any."""
|
||||
return self._icon
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP
|
||||
|
||||
if self.current_cover_position is not None:
|
||||
supported_features |= SUPPORT_SET_POSITION
|
||||
|
||||
if self.current_cover_tilt_position is not None:
|
||||
supported_features |= TILT_FEATURES
|
||||
|
||||
return supported_features
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling state."""
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_open_cover(self, **kwargs):
|
||||
"""Move the cover up."""
|
||||
self.hass.async_add_job(self._open_script.async_run())
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_close_cover(self, **kwargs):
|
||||
"""Move the cover down."""
|
||||
self.hass.async_add_job(self._close_script.async_run())
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_stop_cover(self, **kwargs):
|
||||
"""Fire the stop action."""
|
||||
self.hass.async_add_job(self._stop_script.async_run())
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_set_cover_position(self, **kwargs):
|
||||
"""Set cover position."""
|
||||
if ATTR_POSITION not in kwargs:
|
||||
return
|
||||
self._position = kwargs[ATTR_POSITION]
|
||||
self.hass.async_add_job(self._position_script.async_run(
|
||||
{"position": self._position}))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_open_cover_tilt(self, **kwargs):
|
||||
"""Tilt the cover open."""
|
||||
self._tilt_value = 100
|
||||
self.hass.async_add_job(self._tilt_script.async_run(
|
||||
{"tilt": self._tilt_value}))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_close_cover_tilt(self, **kwargs):
|
||||
"""Tilt the cover closed."""
|
||||
self._tilt_value = 0
|
||||
self.hass.async_add_job(self._tilt_script.async_run(
|
||||
{"tilt": self._tilt_value}))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_set_cover_tilt_position(self, **kwargs):
|
||||
"""Move the cover tilt to a specific position."""
|
||||
if ATTR_TILT_POSITION not in kwargs:
|
||||
return
|
||||
self._tilt_value = kwargs[ATTR_TILT_POSITION]
|
||||
self.hass.async_add_job(self._tilt_script.async_run(
|
||||
{"tilt": self._tilt_value}))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Update the state from the template."""
|
||||
if self._template is not None:
|
||||
try:
|
||||
state = self._template.async_render().lower()
|
||||
if state in _VALID_STATES:
|
||||
if state in ('true', STATE_OPEN):
|
||||
self._position = 100
|
||||
else:
|
||||
self._position = 0
|
||||
else:
|
||||
_LOGGER.error(
|
||||
'Received invalid cover is_on state: %s. Expected: %s',
|
||||
state, ', '.join(_VALID_STATES))
|
||||
self._position = None
|
||||
except TemplateError as ex:
|
||||
_LOGGER.error(ex)
|
||||
self._position = None
|
||||
if self._position_template is not None:
|
||||
try:
|
||||
state = float(self._position_template.async_render())
|
||||
if state < 0 or state > 100:
|
||||
self._position = None
|
||||
_LOGGER.error("Cover position value must be"
|
||||
" between 0 and 100."
|
||||
" Value was: %.2f", state)
|
||||
else:
|
||||
self._position = state
|
||||
except TemplateError as ex:
|
||||
_LOGGER.error(ex)
|
||||
self._position = None
|
||||
except ValueError as ex:
|
||||
_LOGGER.error(ex)
|
||||
self._position = None
|
||||
if self._tilt_template is not None:
|
||||
try:
|
||||
state = float(self._tilt_template.async_render())
|
||||
if state < 0 or state > 100:
|
||||
self._tilt_value = None
|
||||
_LOGGER.error("Tilt value must be between 0 and 100."
|
||||
" Value was: %.2f", state)
|
||||
else:
|
||||
self._tilt_value = state
|
||||
except TemplateError as ex:
|
||||
_LOGGER.error(ex)
|
||||
self._tilt_value = None
|
||||
except ValueError as ex:
|
||||
_LOGGER.error(ex)
|
||||
self._tilt_value = None
|
||||
if self._icon_template is not None:
|
||||
try:
|
||||
self._icon = self._icon_template.async_render()
|
||||
except TemplateError as ex:
|
||||
if ex.args and ex.args[0].startswith(
|
||||
"UndefinedError: 'None' has no attribute"):
|
||||
# Common during HA startup - so just a warning
|
||||
_LOGGER.warning('Could not render icon template %s,'
|
||||
' the state is unknown.', self._name)
|
||||
return
|
||||
self._icon = super().icon
|
||||
_LOGGER.error('Could not render icon template %s: %s',
|
||||
self._name, ex)
|
630
tests/components/cover/test_template.py
Normal file
630
tests/components/cover/test_template.py
Normal file
@@ -0,0 +1,630 @@
|
||||
"""The tests the cover command line platform."""
|
||||
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant import setup
|
||||
import homeassistant.components.cover as cover
|
||||
from homeassistant.const import STATE_OPEN, STATE_CLOSED
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestTemplateCover(unittest.TestCase):
|
||||
"""Test the cover command line platform."""
|
||||
|
||||
hass = None
|
||||
calls = None
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
def setup_method(self, method):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.calls = []
|
||||
|
||||
@callback
|
||||
def record_call(service):
|
||||
"""Track function calls.."""
|
||||
self.calls.append(service)
|
||||
|
||||
self.hass.services.register('test', 'automation', record_call)
|
||||
|
||||
def teardown_method(self, method):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_template_state_text(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'value_template':
|
||||
"{{ states.cover.test_state.state }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.set('cover.test_state', STATE_OPEN)
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.state == STATE_OPEN
|
||||
|
||||
state = self.hass.states.set('cover.test_state', STATE_CLOSED)
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.state == STATE_CLOSED
|
||||
|
||||
def test_template_state_boolean(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'value_template':
|
||||
"{{ 1 == 1 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.state == STATE_OPEN
|
||||
|
||||
def test_template_position(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ states.cover.test.attributes.position }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.set('cover.test', STATE_CLOSED)
|
||||
self.hass.block_till_done()
|
||||
|
||||
entity = self.hass.states.get('cover.test')
|
||||
attrs = dict()
|
||||
attrs['position'] = 42
|
||||
self.hass.states.async_set(
|
||||
entity.entity_id, entity.state,
|
||||
attributes=attrs)
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.attributes.get('current_position') == 42.0
|
||||
assert state.state == STATE_OPEN
|
||||
|
||||
state = self.hass.states.set('cover.test', STATE_OPEN)
|
||||
self.hass.block_till_done()
|
||||
entity = self.hass.states.get('cover.test')
|
||||
attrs['position'] = 0.0
|
||||
self.hass.states.async_set(
|
||||
entity.entity_id, entity.state,
|
||||
attributes=attrs)
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.attributes.get('current_position') == 0.0
|
||||
assert state.state == STATE_CLOSED
|
||||
|
||||
def test_template_tilt(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'value_template':
|
||||
"{{ 1 == 1 }}",
|
||||
'tilt_template':
|
||||
"{{ 42 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.attributes.get('current_tilt_position') == 42.0
|
||||
|
||||
def test_template_out_of_bounds(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ -1 }}",
|
||||
'tilt_template':
|
||||
"{{ 110 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.attributes.get('current_tilt_position') is None
|
||||
assert state.attributes.get('current_position') is None
|
||||
|
||||
def test_template_mutex(self):
|
||||
"""Test that only value or position template can be used."""
|
||||
with assert_setup_component(0, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'value_template':
|
||||
"{{ 1 == 1 }}",
|
||||
'position_template':
|
||||
"{{ 42 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'icon_template':
|
||||
"{% if states.cover.test_state.state %}"
|
||||
"mdi:check"
|
||||
"{% endif %}"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert self.hass.states.all() == []
|
||||
|
||||
def test_template_position_or_value(self):
|
||||
"""Test that at least one of value or position template is used."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'icon_template':
|
||||
"{% if states.cover.test_state.state %}"
|
||||
"mdi:check"
|
||||
"{% endif %}"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert self.hass.states.all() == []
|
||||
|
||||
def test_template_non_numeric(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ on }}",
|
||||
'tilt_template':
|
||||
"{% if states.cover.test_state.state %}"
|
||||
"on"
|
||||
"{% else %}"
|
||||
"off"
|
||||
"{% endif %}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.attributes.get('current_tilt_position') is None
|
||||
assert state.attributes.get('current_position') is None
|
||||
|
||||
def test_open_action(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ 0 }}",
|
||||
'open_cover': {
|
||||
'service': 'test.automation',
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.state == STATE_CLOSED
|
||||
|
||||
cover.open_cover(self.hass, 'cover.test_template_cover')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert len(self.calls) == 1
|
||||
|
||||
def test_close_stop_action(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ 100 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'test.automation',
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'test.automation',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.state == STATE_OPEN
|
||||
|
||||
cover.close_cover(self.hass, 'cover.test_template_cover')
|
||||
self.hass.block_till_done()
|
||||
|
||||
cover.stop_cover(self.hass, 'cover.test_template_cover')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert len(self.calls) == 2
|
||||
|
||||
def test_set_position(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ 100 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.stop_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'set_cover_position': {
|
||||
'service': 'test.automation',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.state == STATE_OPEN
|
||||
|
||||
cover.set_cover_position(self.hass, 42,
|
||||
'cover.test_template_cover')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert len(self.calls) == 1
|
||||
|
||||
def test_set_tilt_position(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ 100 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.stop_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'set_cover_tilt_position': {
|
||||
'service': 'test.automation',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
cover.set_cover_tilt_position(self.hass, 42,
|
||||
'cover.test_template_cover')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert len(self.calls) == 1
|
||||
|
||||
def test_open_tilt_action(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ 100 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.stop_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'set_cover_tilt_position': {
|
||||
'service': 'test.automation',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
cover.open_cover_tilt(self.hass, 'cover.test_template_cover')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert len(self.calls) == 1
|
||||
|
||||
def test_close_tilt_action(self):
|
||||
"""Test the state text of a template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'position_template':
|
||||
"{{ 100 }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.stop_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'set_cover_tilt_position': {
|
||||
'service': 'test.automation',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
cover.close_cover_tilt(self.hass, 'cover.test_template_cover')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert len(self.calls) == 1
|
||||
|
||||
def test_icon_template(self):
|
||||
"""Test icon template."""
|
||||
with assert_setup_component(1, 'cover'):
|
||||
assert setup.setup_component(self.hass, 'cover', {
|
||||
'cover': {
|
||||
'platform': 'template',
|
||||
'covers': {
|
||||
'test_template_cover': {
|
||||
'value_template':
|
||||
"{{ states.cover.test_state.state }}",
|
||||
'open_cover': {
|
||||
'service': 'cover.open_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'close_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'stop_cover': {
|
||||
'service': 'cover.close_cover',
|
||||
'entity_id': 'cover.test_state'
|
||||
},
|
||||
'icon_template':
|
||||
"{% if states.cover.test_state.state %}"
|
||||
"mdi:check"
|
||||
"{% endif %}"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.hass.start()
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
assert state.attributes.get('icon') == ''
|
||||
|
||||
state = self.hass.states.set('cover.test_state', STATE_OPEN)
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('cover.test_template_cover')
|
||||
|
||||
assert state.attributes['icon'] == 'mdi:check'
|
Reference in New Issue
Block a user