From b67c5df5257d58a172ea9f00d8229700f2d0532f Mon Sep 17 00:00:00 2001 From: PhracturedBlue Date: Thu, 6 Jul 2017 22:35:59 -0700 Subject: [PATCH] cover_template:i open/close/stop actions no longer required. Improve tests (#8344) --- homeassistant/components/cover/template.py | 55 ++++--- tests/components/cover/test_template.py | 169 +++++++++++---------- 2 files changed, 122 insertions(+), 102 deletions(-) diff --git a/homeassistant/components/cover/template.py b/homeassistant/components/cover/template.py index fd746131288..769c2fc4ed6 100644 --- a/homeassistant/components/cover/template.py +++ b/homeassistant/components/cover/template.py @@ -40,14 +40,15 @@ STOP_ACTION = 'stop_cover' POSITION_ACTION = 'set_cover_position' TILT_ACTION = 'set_cover_tilt_position' CONF_VALUE_OR_POSITION_TEMPLATE = 'value_or_position' +CONF_OPEN_OR_CLOSE = 'open_or_close' 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.Inclusive(OPEN_ACTION, CONF_OPEN_OR_CLOSE): cv.SCRIPT_SCHEMA, + vol.Inclusive(CLOSE_ACTION, CONF_OPEN_OR_CLOSE): cv.SCRIPT_SCHEMA, + vol.Optional(STOP_ACTION): cv.SCRIPT_SCHEMA, vol.Exclusive(CONF_POSITION_TEMPLATE, CONF_VALUE_OR_POSITION_TEMPLATE): cv.template, vol.Exclusive(CONF_VALUE_TEMPLATE, @@ -77,9 +78,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): 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] + open_action = device_config.get(OPEN_ACTION) + close_action = device_config.get(CLOSE_ACTION) + stop_action = device_config.get(STOP_ACTION) position_action = device_config.get(POSITION_ACTION) tilt_action = device_config.get(TILT_ACTION) @@ -88,6 +89,10 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): CONF_VALUE_TEMPLATE, CONF_VALUE_TEMPLATE) continue + if position_action is None and open_action is None: + _LOGGER.error('Must specify at least one of %s' or '%s', + OPEN_ACTION, POSITION_ACTION) + continue template_entity_ids = set() if state_template is not None: temp_ids = state_template.extract_entities() @@ -147,9 +152,15 @@ class CoverTemplate(CoverDevice): 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._open_script = None + if open_action is not None: + self._open_script = Script(hass, open_action) + self._close_script = None + if close_action is not None: + self._close_script = Script(hass, close_action) + self._stop_script = None + if stop_action is not None: + self._stop_script = Script(hass, stop_action) self._position_script = None if position_action is not None: self._position_script = Script(hass, position_action) @@ -227,9 +238,12 @@ class CoverTemplate(CoverDevice): @property def supported_features(self): """Flag supported features.""" - supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP + supported_features = SUPPORT_OPEN | SUPPORT_CLOSE - if self.current_cover_position is not None: + if self._stop_script is not None: + supported_features |= SUPPORT_STOP + + if self._position_script is not None: supported_features |= SUPPORT_SET_POSITION if self.current_cover_tilt_position is not None: @@ -245,23 +259,30 @@ class CoverTemplate(CoverDevice): @asyncio.coroutine def async_open_cover(self, **kwargs): """Move the cover up.""" - self.hass.async_add_job(self._open_script.async_run()) + if self._open_script: + self.hass.async_add_job(self._open_script.async_run()) + elif self._position_script: + self.hass.async_add_job(self._position_script.async_run( + {"position": 100})) @asyncio.coroutine def async_close_cover(self, **kwargs): """Move the cover down.""" - self.hass.async_add_job(self._close_script.async_run()) + if self._close_script: + self.hass.async_add_job(self._close_script.async_run()) + elif self._position_script: + self.hass.async_add_job(self._position_script.async_run( + {"position": 0})) @asyncio.coroutine def async_stop_cover(self, **kwargs): """Fire the stop action.""" - self.hass.async_add_job(self._stop_script.async_run()) + if self._stop_script: + 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})) @@ -283,8 +304,6 @@ class CoverTemplate(CoverDevice): @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})) diff --git a/tests/components/cover/test_template.py b/tests/components/cover/test_template.py index 35ec21bfbdf..cd2120e71e6 100644 --- a/tests/components/cover/test_template.py +++ b/tests/components/cover/test_template.py @@ -54,10 +54,6 @@ class TestTemplateCover(unittest.TestCase): 'service': 'cover.close_cover', 'entity_id': 'cover.test_state' }, - 'stop_cover': { - 'service': 'cover.close_cover', - 'entity_id': 'cover.test_state' - }, } } } @@ -79,7 +75,7 @@ class TestTemplateCover(unittest.TestCase): assert state.state == STATE_CLOSED def test_template_state_boolean(self): - """Test the state text of a template.""" + """Test the value_template attribute.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -96,10 +92,6 @@ class TestTemplateCover(unittest.TestCase): 'service': 'cover.close_cover', 'entity_id': 'cover.test_state' }, - 'stop_cover': { - 'service': 'cover.close_cover', - 'entity_id': 'cover.test_state' - }, } } } @@ -112,7 +104,7 @@ class TestTemplateCover(unittest.TestCase): assert state.state == STATE_OPEN def test_template_position(self): - """Test the state text of a template.""" + """Test the position_template attribute.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -129,10 +121,6 @@ class TestTemplateCover(unittest.TestCase): 'service': 'cover.close_cover', 'entity_id': 'cover.test' }, - 'stop_cover': { - 'service': 'cover.close_cover', - 'entity_id': 'cover.test' - }, } } } @@ -170,7 +158,7 @@ class TestTemplateCover(unittest.TestCase): assert state.state == STATE_CLOSED def test_template_tilt(self): - """Test the state text of a template.""" + """Test the tilt_template attribute.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -189,10 +177,6 @@ class TestTemplateCover(unittest.TestCase): 'service': 'cover.close_cover', 'entity_id': 'cover.test_state' }, - 'stop_cover': { - 'service': 'cover.close_cover', - 'entity_id': 'cover.test_state' - }, } } } @@ -205,7 +189,7 @@ class TestTemplateCover(unittest.TestCase): assert state.attributes.get('current_tilt_position') == 42.0 def test_template_out_of_bounds(self): - """Test the state text of a template.""" + """Test template out-of-bounds condition.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -224,10 +208,6 @@ class TestTemplateCover(unittest.TestCase): 'service': 'cover.close_cover', 'entity_id': 'cover.test_state' }, - 'stop_cover': { - 'service': 'cover.close_cover', - 'entity_id': 'cover.test_state' - }, } } } @@ -260,10 +240,6 @@ class TestTemplateCover(unittest.TestCase): '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" @@ -294,14 +270,26 @@ class TestTemplateCover(unittest.TestCase): '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_open_or_position(self): + """Test that at least one of open_cover or set_position is used.""" + with assert_setup_component(1, 'cover'): + assert setup.setup_component(self.hass, 'cover', { + 'cover': { + 'platform': 'template', + 'covers': { + 'test_template_cover': { + 'value_template': + "{{ 1 == 1 }}", } } } @@ -312,8 +300,32 @@ class TestTemplateCover(unittest.TestCase): assert self.hass.states.all() == [] + def test_template_open_and_close(self): + """Test that if open_cover is specified, cose_cover is too.""" + with assert_setup_component(0, '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' + }, + }, + } + } + }) + + 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.""" + """Test that tilt_template values are numeric.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -336,10 +348,6 @@ class TestTemplateCover(unittest.TestCase): 'service': 'cover.close_cover', 'entity_id': 'cover.test_state' }, - 'stop_cover': { - 'service': 'cover.close_cover', - 'entity_id': 'cover.test_state' - }, } } } @@ -353,7 +361,7 @@ class TestTemplateCover(unittest.TestCase): assert state.attributes.get('current_position') is None def test_open_action(self): - """Test the state text of a template.""" + """Test the open_cover command.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -369,10 +377,6 @@ class TestTemplateCover(unittest.TestCase): 'service': 'cover.close_cover', 'entity_id': 'cover.test_state' }, - 'stop_cover': { - 'service': 'cover.close_cover', - 'entity_id': 'cover.test_state' - }, } } } @@ -390,7 +394,7 @@ class TestTemplateCover(unittest.TestCase): assert len(self.calls) == 1 def test_close_stop_action(self): - """Test the state text of a template.""" + """Test the close-cover and stop_cover commands.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -429,29 +433,30 @@ class TestTemplateCover(unittest.TestCase): assert len(self.calls) == 2 def test_set_position(self): - """Test the state text of a template.""" + """Test the set_position command.""" with assert_setup_component(1, 'cover'): + assert setup.setup_component(self.hass, 'input_slider', { + 'input_slider': { + 'test': { + 'min': '0', + 'max': '100', + 'initial': '42', + } + } + }) 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' - }, + "{{ states.input_slider.test.state | int }}", 'set_cover_position': { - 'service': 'test.automation', + 'service': 'input_slider.select_value', + 'entity_id': 'input_slider.test', + 'data_template': { + 'value': '{{ position }}' + }, }, } } @@ -461,17 +466,29 @@ class TestTemplateCover(unittest.TestCase): self.hass.start() self.hass.block_till_done() + state = self.hass.states.set('input_slider.test', 42) + 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.open_cover(self.hass, 'cover.test_template_cover') + self.hass.block_till_done() + state = self.hass.states.get('cover.test_template_cover') + assert state.attributes.get('current_position') == 100.0 + + cover.close_cover(self.hass, 'cover.test_template_cover') + self.hass.block_till_done() + state = self.hass.states.get('cover.test_template_cover') + assert state.attributes.get('current_position') == 0.0 + + cover.set_cover_position(self.hass, 25, 'cover.test_template_cover') self.hass.block_till_done() - - assert len(self.calls) == 1 + state = self.hass.states.get('cover.test_template_cover') + assert state.attributes.get('current_position') == 25.0 def test_set_tilt_position(self): - """Test the state text of a template.""" + """Test the set_tilt_position command.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -488,10 +505,6 @@ class TestTemplateCover(unittest.TestCase): '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', }, @@ -510,7 +523,7 @@ class TestTemplateCover(unittest.TestCase): assert len(self.calls) == 1 def test_open_tilt_action(self): - """Test the state text of a template.""" + """Test the open_cover_tilt command.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -527,10 +540,6 @@ class TestTemplateCover(unittest.TestCase): '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', }, @@ -548,7 +557,7 @@ class TestTemplateCover(unittest.TestCase): assert len(self.calls) == 1 def test_close_tilt_action(self): - """Test the state text of a template.""" + """Test the close_cover_tilt command.""" with assert_setup_component(1, 'cover'): assert setup.setup_component(self.hass, 'cover', { 'cover': { @@ -565,10 +574,6 @@ class TestTemplateCover(unittest.TestCase): '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', }, @@ -603,10 +608,6 @@ class TestTemplateCover(unittest.TestCase): '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"