diff --git a/tests/components/fan/common.py b/tests/components/fan/common.py index 60e1cab1ac0..f3873dd9fe0 100644 --- a/tests/components/fan/common.py +++ b/tests/components/fan/common.py @@ -9,10 +9,12 @@ from homeassistant.components.fan import ( from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF) from homeassistant.loader import bind_hass +from homeassistant.core import callback +@callback @bind_hass -def turn_on(hass, entity_id: str = None, speed: str = None) -> None: +def async_turn_on(hass, entity_id: str = None, speed: str = None) -> None: """Turn all or specified fan on.""" data = { key: value for key, value in [ @@ -21,20 +23,24 @@ def turn_on(hass, entity_id: str = None, speed: str = None) -> None: ] if value is not None } - hass.services.call(DOMAIN, SERVICE_TURN_ON, data) + hass.async_create_task( + hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data)) +@callback @bind_hass -def turn_off(hass, entity_id: str = None) -> None: +def async_turn_off(hass, entity_id: str = None) -> None: """Turn all or specified fan off.""" data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} - hass.services.call(DOMAIN, SERVICE_TURN_OFF, data) + hass.async_create_task( + hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data)) +@callback @bind_hass -def oscillate(hass, entity_id: str = None, - should_oscillate: bool = True) -> None: +def async_oscillate(hass, entity_id: str = None, + should_oscillate: bool = True) -> None: """Set oscillation on all or specified fan.""" data = { key: value for key, value in [ @@ -43,11 +49,13 @@ def oscillate(hass, entity_id: str = None, ] if value is not None } - hass.services.call(DOMAIN, SERVICE_OSCILLATE, data) + hass.async_create_task( + hass.services.async_call(DOMAIN, SERVICE_OSCILLATE, data)) +@callback @bind_hass -def set_speed(hass, entity_id: str = None, speed: str = None) -> None: +def async_set_speed(hass, entity_id: str = None, speed: str = None) -> None: """Set speed for all or specified fan.""" data = { key: value for key, value in [ @@ -56,11 +64,14 @@ def set_speed(hass, entity_id: str = None, speed: str = None) -> None: ] if value is not None } - hass.services.call(DOMAIN, SERVICE_SET_SPEED, data) + hass.async_create_task( + hass.services.async_call(DOMAIN, SERVICE_SET_SPEED, data)) +@callback @bind_hass -def set_direction(hass, entity_id: str = None, direction: str = None) -> None: +def async_set_direction( + hass, entity_id: str = None, direction: str = None) -> None: """Set direction for all or specified fan.""" data = { key: value for key, value in [ @@ -69,4 +80,5 @@ def set_direction(hass, entity_id: str = None, direction: str = None) -> None: ] if value is not None } - hass.services.call(DOMAIN, SERVICE_SET_DIRECTION, data) + hass.async_create_task( + hass.services.async_call(DOMAIN, SERVICE_SET_DIRECTION, data)) diff --git a/tests/components/fan/test_demo.py b/tests/components/fan/test_demo.py index 35835ac37e5..5a819b0c5da 100644 --- a/tests/components/fan/test_demo.py +++ b/tests/components/fan/test_demo.py @@ -1,108 +1,108 @@ """Test cases around the demo fan platform.""" +import pytest -import unittest - -from homeassistant.setup import setup_component +from homeassistant.setup import async_setup_component from homeassistant.components import fan from homeassistant.const import STATE_OFF, STATE_ON -from tests.common import get_test_home_assistant from tests.components.fan import common FAN_ENTITY_ID = 'fan.living_room_fan' -class TestDemoFan(unittest.TestCase): - """Test the fan demo platform.""" +def get_entity(hass): + """Get the fan entity.""" + return hass.states.get(FAN_ENTITY_ID) - def get_entity(self): - """Get the fan entity.""" - return self.hass.states.get(FAN_ENTITY_ID) - def setUp(self): - """Initialize unit test data.""" - self.hass = get_test_home_assistant() - assert setup_component(self.hass, fan.DOMAIN, {'fan': { +@pytest.fixture(autouse=True) +def setup_comp(hass): + """Initialize components.""" + hass.loop.run_until_complete(async_setup_component(hass, fan.DOMAIN, { + 'fan': { 'platform': 'demo', - }}) - self.hass.block_till_done() + } + })) - def tearDown(self): - """Tear down unit test data.""" - self.hass.stop() - def test_turn_on(self): - """Test turning on the device.""" - assert STATE_OFF == self.get_entity().state +async def test_turn_on(hass): + """Test turning on the device.""" + assert STATE_OFF == get_entity(hass).state - common.turn_on(self.hass, FAN_ENTITY_ID) - self.hass.block_till_done() - assert STATE_OFF != self.get_entity().state + common.async_turn_on(hass, FAN_ENTITY_ID) + await hass.async_block_till_done() + assert STATE_OFF != get_entity(hass).state - common.turn_on(self.hass, FAN_ENTITY_ID, fan.SPEED_HIGH) - self.hass.block_till_done() - assert STATE_ON == self.get_entity().state - assert fan.SPEED_HIGH == \ - self.get_entity().attributes[fan.ATTR_SPEED] + common.async_turn_on(hass, FAN_ENTITY_ID, fan.SPEED_HIGH) + await hass.async_block_till_done() + assert STATE_ON == get_entity(hass).state + assert fan.SPEED_HIGH == \ + get_entity(hass).attributes[fan.ATTR_SPEED] - def test_turn_off(self): - """Test turning off the device.""" - assert STATE_OFF == self.get_entity().state - common.turn_on(self.hass, FAN_ENTITY_ID) - self.hass.block_till_done() - assert STATE_OFF != self.get_entity().state +async def test_turn_off(hass): + """Test turning off the device.""" + assert STATE_OFF == get_entity(hass).state - common.turn_off(self.hass, FAN_ENTITY_ID) - self.hass.block_till_done() - assert STATE_OFF == self.get_entity().state + common.async_turn_on(hass, FAN_ENTITY_ID) + await hass.async_block_till_done() + assert STATE_OFF != get_entity(hass).state - def test_turn_off_without_entity_id(self): - """Test turning off all fans.""" - assert STATE_OFF == self.get_entity().state + common.async_turn_off(hass, FAN_ENTITY_ID) + await hass.async_block_till_done() + assert STATE_OFF == get_entity(hass).state - common.turn_on(self.hass, FAN_ENTITY_ID) - self.hass.block_till_done() - assert STATE_OFF != self.get_entity().state - common.turn_off(self.hass) - self.hass.block_till_done() - assert STATE_OFF == self.get_entity().state +async def test_turn_off_without_entity_id(hass): + """Test turning off all fans.""" + assert STATE_OFF == get_entity(hass).state - def test_set_direction(self): - """Test setting the direction of the device.""" - assert STATE_OFF == self.get_entity().state + common.async_turn_on(hass, FAN_ENTITY_ID) + await hass.async_block_till_done() + assert STATE_OFF != get_entity(hass).state - common.set_direction(self.hass, FAN_ENTITY_ID, fan.DIRECTION_REVERSE) - self.hass.block_till_done() - assert fan.DIRECTION_REVERSE == \ - self.get_entity().attributes.get('direction') + common.async_turn_off(hass) + await hass.async_block_till_done() + assert STATE_OFF == get_entity(hass).state - def test_set_speed(self): - """Test setting the speed of the device.""" - assert STATE_OFF == self.get_entity().state - common.set_speed(self.hass, FAN_ENTITY_ID, fan.SPEED_LOW) - self.hass.block_till_done() - assert fan.SPEED_LOW == \ - self.get_entity().attributes.get('speed') +async def test_set_direction(hass): + """Test setting the direction of the device.""" + assert STATE_OFF == get_entity(hass).state - def test_oscillate(self): - """Test oscillating the fan.""" - assert not self.get_entity().attributes.get('oscillating') + common.async_set_direction(hass, FAN_ENTITY_ID, fan.DIRECTION_REVERSE) + await hass.async_block_till_done() + assert fan.DIRECTION_REVERSE == \ + get_entity(hass).attributes.get('direction') - common.oscillate(self.hass, FAN_ENTITY_ID, True) - self.hass.block_till_done() - assert self.get_entity().attributes.get('oscillating') - common.oscillate(self.hass, FAN_ENTITY_ID, False) - self.hass.block_till_done() - assert not self.get_entity().attributes.get('oscillating') +async def test_set_speed(hass): + """Test setting the speed of the device.""" + assert STATE_OFF == get_entity(hass).state - def test_is_on(self): - """Test is on service call.""" - assert not fan.is_on(self.hass, FAN_ENTITY_ID) + common.async_set_speed(hass, FAN_ENTITY_ID, fan.SPEED_LOW) + await hass.async_block_till_done() + assert fan.SPEED_LOW == \ + get_entity(hass).attributes.get('speed') - common.turn_on(self.hass, FAN_ENTITY_ID) - self.hass.block_till_done() - assert fan.is_on(self.hass, FAN_ENTITY_ID) + +async def test_oscillate(hass): + """Test oscillating the fan.""" + assert not get_entity(hass).attributes.get('oscillating') + + common.async_oscillate(hass, FAN_ENTITY_ID, True) + await hass.async_block_till_done() + assert get_entity(hass).attributes.get('oscillating') + + common.async_oscillate(hass, FAN_ENTITY_ID, False) + await hass.async_block_till_done() + assert not get_entity(hass).attributes.get('oscillating') + + +async def test_is_on(hass): + """Test is on service call.""" + assert not fan.is_on(hass, FAN_ENTITY_ID) + + common.async_turn_on(hass, FAN_ENTITY_ID) + await hass.async_block_till_done() + assert fan.is_on(hass, FAN_ENTITY_ID) diff --git a/tests/components/fan/test_template.py b/tests/components/fan/test_template.py index 09d3603e004..85e63025bbc 100644 --- a/tests/components/fan/test_template.py +++ b/tests/components/fan/test_template.py @@ -1,7 +1,7 @@ """The tests for the Template fan platform.""" import logging +import pytest -from homeassistant.core import callback from homeassistant import setup from homeassistant.const import STATE_ON, STATE_OFF from homeassistant.components.fan import ( @@ -9,7 +9,7 @@ from homeassistant.components.fan import ( ATTR_DIRECTION, DIRECTION_FORWARD, DIRECTION_REVERSE) from tests.common import ( - get_test_home_assistant, assert_setup_component) + async_mock_service, assert_setup_component) from tests.components.fan import common _LOGGER = logging.getLogger(__name__) @@ -26,129 +26,93 @@ _OSC_INPUT = 'input_select.osc' _DIRECTION_INPUT_SELECT = 'input_select.direction' -class TestTemplateFan: - """Test the Template light.""" +@pytest.fixture +def calls(hass): + """Track calls to a mock serivce.""" + return async_mock_service(hass, 'test', 'automation') - hass = None - calls = None - # pylint: disable=invalid-name - def setup_method(self, method): - """Set up.""" - self.hass = get_test_home_assistant() +# Configuration tests # +async def test_missing_optional_config(hass, calls): + """Test: missing optional template is ok.""" + with assert_setup_component(1, 'fan'): + assert await setup.async_setup_component(hass, 'fan', { + 'fan': { + 'platform': 'template', + 'fans': { + 'test_fan': { + 'value_template': "{{ 'on' }}", - 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() - - # Configuration tests # - def test_missing_optional_config(self): - """Test: missing optional template is ok.""" - with assert_setup_component(1, 'fan'): - assert setup.setup_component(self.hass, 'fan', { - 'fan': { - 'platform': 'template', - 'fans': { - 'test_fan': { - 'value_template': "{{ 'on' }}", - - 'turn_on': { - 'service': 'script.fan_on' - }, - 'turn_off': { - 'service': 'script.fan_off' - } + 'turn_on': { + 'service': 'script.fan_on' + }, + 'turn_off': { + 'service': 'script.fan_off' } } } - }) + } + }) - self.hass.start() - self.hass.block_till_done() + await hass.async_start() + await hass.async_block_till_done() - self._verify(STATE_ON, None, None, None) + _verify(hass, STATE_ON, None, None, None) - def test_missing_value_template_config(self): - """Test: missing 'value_template' will fail.""" - with assert_setup_component(0, 'fan'): - assert setup.setup_component(self.hass, 'fan', { - 'fan': { - 'platform': 'template', - 'fans': { - 'test_fan': { - 'turn_on': { - 'service': 'script.fan_on' - }, - 'turn_off': { - 'service': 'script.fan_off' - } + +async def test_missing_value_template_config(hass, calls): + """Test: missing 'value_template' will fail.""" + with assert_setup_component(0, 'fan'): + assert await setup.async_setup_component(hass, 'fan', { + 'fan': { + 'platform': 'template', + 'fans': { + 'test_fan': { + 'turn_on': { + 'service': 'script.fan_on' + }, + 'turn_off': { + 'service': 'script.fan_off' } } } - }) + } + }) - self.hass.start() - self.hass.block_till_done() + await hass.async_start() + await hass.async_block_till_done() - assert self.hass.states.all() == [] + assert hass.states.async_all() == [] - def test_missing_turn_on_config(self): - """Test: missing 'turn_on' will fail.""" - with assert_setup_component(0, 'fan'): - assert setup.setup_component(self.hass, 'fan', { - 'fan': { - 'platform': 'template', - 'fans': { - 'test_fan': { - 'value_template': "{{ 'on' }}", - 'turn_off': { - 'service': 'script.fan_off' - } + +async def test_missing_turn_on_config(hass, calls): + """Test: missing 'turn_on' will fail.""" + with assert_setup_component(0, 'fan'): + assert await setup.async_setup_component(hass, 'fan', { + 'fan': { + 'platform': 'template', + 'fans': { + 'test_fan': { + 'value_template': "{{ 'on' }}", + 'turn_off': { + 'service': 'script.fan_off' } } } - }) + } + }) - self.hass.start() - self.hass.block_till_done() + await hass.async_start() + await hass.async_block_till_done() - assert self.hass.states.all() == [] + assert hass.states.async_all() == [] - def test_missing_turn_off_config(self): - """Test: missing 'turn_off' will fail.""" - with assert_setup_component(0, 'fan'): - assert setup.setup_component(self.hass, 'fan', { - 'fan': { - 'platform': 'template', - 'fans': { - 'test_fan': { - 'value_template': "{{ 'on' }}", - 'turn_on': { - 'service': 'script.fan_on' - } - } - } - } - }) - self.hass.start() - self.hass.block_till_done() - - assert self.hass.states.all() == [] - - def test_invalid_config(self): - """Test: missing 'turn_off' will fail.""" - with assert_setup_component(0, 'fan'): - assert setup.setup_component(self.hass, 'fan', { +async def test_missing_turn_off_config(hass, calls): + """Test: missing 'turn_off' will fail.""" + with assert_setup_component(0, 'fan'): + assert await setup.async_setup_component(hass, 'fan', { + 'fan': { 'platform': 'template', 'fans': { 'test_fan': { @@ -158,488 +122,527 @@ class TestTemplateFan: } } } - }) + } + }) - self.hass.start() - self.hass.block_till_done() + await hass.async_start() + await hass.async_block_till_done() - assert self.hass.states.all() == [] + assert hass.states.async_all() == [] - # End of configuration tests # - # Template tests # - def test_templates_with_entities(self): - """Test tempalates with values from other entities.""" - value_template = """ - {% if is_state('input_boolean.state', 'True') %} - {{ 'on' }} - {% else %} - {{ 'off' }} - {% endif %} - """ - - with assert_setup_component(1, 'fan'): - assert setup.setup_component(self.hass, 'fan', { - 'fan': { - 'platform': 'template', - 'fans': { - 'test_fan': { - 'value_template': value_template, - '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' - } - } - } - } - }) - - self.hass.start() - self.hass.block_till_done() - - self._verify(STATE_OFF, None, None, None) - - self.hass.states.set(_STATE_INPUT_BOOLEAN, True) - self.hass.states.set(_SPEED_INPUT_SELECT, SPEED_MEDIUM) - self.hass.states.set(_OSC_INPUT, 'True') - self.hass.states.set(_DIRECTION_INPUT_SELECT, DIRECTION_FORWARD) - self.hass.block_till_done() - - self._verify(STATE_ON, SPEED_MEDIUM, True, DIRECTION_FORWARD) - - def test_templates_with_valid_values(self): - """Test templates with valid values.""" - with assert_setup_component(1, 'fan'): - assert setup.setup_component(self.hass, 'fan', { - 'fan': { - 'platform': 'template', - 'fans': { - 'test_fan': { - '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' - } - } - } - } - }) - - self.hass.start() - self.hass.block_till_done() - - self._verify(STATE_ON, SPEED_MEDIUM, True, DIRECTION_FORWARD) - - def test_templates_invalid_values(self): - """Test templates with invalid values.""" - with assert_setup_component(1, 'fan'): - assert setup.setup_component(self.hass, 'fan', { - 'fan': { - 'platform': 'template', - 'fans': { - 'test_fan': { - 'value_template': - "{{ 'abc' }}", - 'speed_template': - "{{ '0' }}", - 'oscillating_template': - "{{ 'xyz' }}", - 'direction_template': - "{{ 'right' }}", - - 'turn_on': { - 'service': 'script.fan_on' - }, - 'turn_off': { - 'service': 'script.fan_off' - } - } - } - } - }) - - self.hass.start() - self.hass.block_till_done() - - self._verify(STATE_OFF, None, None, None) - - # End of template tests # - - # Function tests # - def test_on_off(self): - """Test turn on and turn off.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_STATE_INPUT_BOOLEAN).state == STATE_ON - self._verify(STATE_ON, None, None, None) - - # Turn off fan - common.turn_off(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_STATE_INPUT_BOOLEAN).state == STATE_OFF - self._verify(STATE_OFF, None, None, None) - - def test_on_with_speed(self): - """Test turn on with speed.""" - self._register_components() - - # Turn on fan with high speed - common.turn_on(self.hass, _TEST_FAN, SPEED_HIGH) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_STATE_INPUT_BOOLEAN).state == STATE_ON - assert self.hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_HIGH - self._verify(STATE_ON, SPEED_HIGH, None, None) - - def test_set_speed(self): - """Test set valid speed.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's speed to high - common.set_speed(self.hass, _TEST_FAN, SPEED_HIGH) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_HIGH - self._verify(STATE_ON, SPEED_HIGH, None, None) - - # Set fan's speed to medium - common.set_speed(self.hass, _TEST_FAN, SPEED_MEDIUM) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_MEDIUM - self._verify(STATE_ON, SPEED_MEDIUM, None, None) - - def test_set_invalid_speed_from_initial_stage(self): - """Test set invalid speed when fan is in initial state.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's speed to 'invalid' - common.set_speed(self.hass, _TEST_FAN, 'invalid') - self.hass.block_till_done() - - # verify speed is unchanged - assert self.hass.states.get(_SPEED_INPUT_SELECT).state == '' - self._verify(STATE_ON, None, None, None) - - def test_set_invalid_speed(self): - """Test set invalid speed when fan has valid speed.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's speed to high - common.set_speed(self.hass, _TEST_FAN, SPEED_HIGH) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_HIGH - self._verify(STATE_ON, SPEED_HIGH, None, None) - - # Set fan's speed to 'invalid' - common.set_speed(self.hass, _TEST_FAN, 'invalid') - self.hass.block_till_done() - - # verify speed is unchanged - assert self.hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_HIGH - self._verify(STATE_ON, SPEED_HIGH, None, None) - - def test_custom_speed_list(self): - """Test set custom speed list.""" - self._register_components(['1', '2', '3']) - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's speed to '1' - common.set_speed(self.hass, _TEST_FAN, '1') - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_SPEED_INPUT_SELECT).state == '1' - self._verify(STATE_ON, '1', None, None) - - # Set fan's speed to 'medium' which is invalid - common.set_speed(self.hass, _TEST_FAN, SPEED_MEDIUM) - self.hass.block_till_done() - - # verify that speed is unchanged - assert self.hass.states.get(_SPEED_INPUT_SELECT).state == '1' - self._verify(STATE_ON, '1', None, None) - - def test_set_osc(self): - """Test set oscillating.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's osc to True - common.oscillate(self.hass, _TEST_FAN, True) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_OSC_INPUT).state == 'True' - self._verify(STATE_ON, None, True, None) - - # Set fan's osc to False - common.oscillate(self.hass, _TEST_FAN, False) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_OSC_INPUT).state == 'False' - self._verify(STATE_ON, None, False, None) - - def test_set_invalid_osc_from_initial_state(self): - """Test set invalid oscillating when fan is in initial state.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's osc to 'invalid' - common.oscillate(self.hass, _TEST_FAN, 'invalid') - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_OSC_INPUT).state == '' - self._verify(STATE_ON, None, None, None) - - def test_set_invalid_osc(self): - """Test set invalid oscillating when fan has valid osc.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's osc to True - common.oscillate(self.hass, _TEST_FAN, True) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_OSC_INPUT).state == 'True' - self._verify(STATE_ON, None, True, None) - - # Set fan's osc to False - common.oscillate(self.hass, _TEST_FAN, None) - self.hass.block_till_done() - - # verify osc is unchanged - assert self.hass.states.get(_OSC_INPUT).state == 'True' - self._verify(STATE_ON, None, True, None) - - def test_set_direction(self): - """Test set valid direction.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's direction to forward - common.set_direction(self.hass, _TEST_FAN, DIRECTION_FORWARD) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_DIRECTION_INPUT_SELECT).state \ - == DIRECTION_FORWARD - self._verify(STATE_ON, None, None, DIRECTION_FORWARD) - - # Set fan's direction to reverse - common.set_direction(self.hass, _TEST_FAN, DIRECTION_REVERSE) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_DIRECTION_INPUT_SELECT).state \ - == DIRECTION_REVERSE - self._verify(STATE_ON, None, None, DIRECTION_REVERSE) - - def test_set_invalid_direction_from_initial_stage(self): - """Test set invalid direction when fan is in initial state.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's direction to 'invalid' - common.set_direction(self.hass, _TEST_FAN, 'invalid') - self.hass.block_till_done() - - # verify direction is unchanged - assert self.hass.states.get(_DIRECTION_INPUT_SELECT).state == '' - self._verify(STATE_ON, None, None, None) - - def test_set_invalid_direction(self): - """Test set invalid direction when fan has valid direction.""" - self._register_components() - - # Turn on fan - common.turn_on(self.hass, _TEST_FAN) - self.hass.block_till_done() - - # Set fan's direction to forward - common.set_direction(self.hass, _TEST_FAN, DIRECTION_FORWARD) - self.hass.block_till_done() - - # verify - assert self.hass.states.get(_DIRECTION_INPUT_SELECT).state == \ - DIRECTION_FORWARD - self._verify(STATE_ON, None, None, DIRECTION_FORWARD) - - # Set fan's direction to 'invalid' - common.set_direction(self.hass, _TEST_FAN, 'invalid') - self.hass.block_till_done() - - # verify direction is unchanged - assert self.hass.states.get(_DIRECTION_INPUT_SELECT).state == \ - DIRECTION_FORWARD - self._verify(STATE_ON, None, None, DIRECTION_FORWARD) - - def _verify(self, expected_state, expected_speed, expected_oscillating, - expected_direction): - """Verify fan's state, speed and osc.""" - state = self.hass.states.get(_TEST_FAN) - attributes = state.attributes - assert state.state == expected_state - assert attributes.get(ATTR_SPEED, None) == expected_speed - assert attributes.get(ATTR_OSCILLATING, None) == expected_oscillating - assert attributes.get(ATTR_DIRECTION, None) == expected_direction - - def _register_components(self, speed_list=None): - """Register basic components for testing.""" - with assert_setup_component(1, 'input_boolean'): - assert setup.setup_component( - self.hass, - 'input_boolean', - {'input_boolean': {'state': None}} - ) - - with assert_setup_component(3, 'input_select'): - assert setup.setup_component(self.hass, 'input_select', { - 'input_select': { - 'speed': { - 'name': 'Speed', - 'options': ['', SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH, - '1', '2', '3'] - }, - - 'osc': { - 'name': 'oscillating', - 'options': ['', 'True', 'False'] - }, - - 'direction': { - 'name': 'Direction', - 'options': ['', DIRECTION_FORWARD, DIRECTION_REVERSE] - }, - } - }) - - with assert_setup_component(1, 'fan'): - value_template = """ - {% if is_state('input_boolean.state', 'on') %} - {{ 'on' }} - {% else %} - {{ 'off' }} - {% endif %} - """ - - test_fan_config = { - 'value_template': value_template, - 'speed_template': - "{{ states('input_select.speed') }}", - 'oscillating_template': - "{{ states('input_select.osc') }}", - 'direction_template': - "{{ states('input_select.direction') }}", - - 'turn_on': { - 'service': 'input_boolean.turn_on', - 'entity_id': _STATE_INPUT_BOOLEAN - }, - 'turn_off': { - 'service': 'input_boolean.turn_off', - 'entity_id': _STATE_INPUT_BOOLEAN - }, - 'set_speed': { - 'service': 'input_select.select_option', - - 'data_template': { - 'entity_id': _SPEED_INPUT_SELECT, - 'option': '{{ speed }}' - } - }, - 'set_oscillating': { - 'service': 'input_select.select_option', - - 'data_template': { - 'entity_id': _OSC_INPUT, - 'option': '{{ oscillating }}' - } - }, - 'set_direction': { - 'service': 'input_select.select_option', - - 'data_template': { - 'entity_id': _DIRECTION_INPUT_SELECT, - 'option': '{{ direction }}' +async def test_invalid_config(hass, calls): + """Test: missing 'turn_off' will fail.""" + with assert_setup_component(0, 'fan'): + assert await setup.async_setup_component(hass, 'fan', { + 'platform': 'template', + 'fans': { + 'test_fan': { + 'value_template': "{{ 'on' }}", + 'turn_on': { + 'service': 'script.fan_on' } } } + }) - if speed_list: - test_fan_config['speeds'] = speed_list + await hass.async_start() + await hass.async_block_till_done() - assert setup.setup_component(self.hass, 'fan', { - 'fan': { - 'platform': 'template', - 'fans': { - 'test_fan': test_fan_config + assert hass.states.async_all() == [] + +# End of configuration tests # + + +# Template tests # +async def test_templates_with_entities(hass, calls): + """Test tempalates with values from other entities.""" + value_template = """ + {% if is_state('input_boolean.state', 'True') %} + {{ 'on' }} + {% else %} + {{ 'off' }} + {% endif %} + """ + + with assert_setup_component(1, 'fan'): + assert await setup.async_setup_component(hass, 'fan', { + 'fan': { + 'platform': 'template', + 'fans': { + 'test_fan': { + 'value_template': value_template, + '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' + } } } - }) + } + }) - self.hass.start() - self.hass.block_till_done() + await hass.async_start() + await hass.async_block_till_done() + + _verify(hass, STATE_OFF, None, None, None) + + hass.states.async_set(_STATE_INPUT_BOOLEAN, True) + hass.states.async_set(_SPEED_INPUT_SELECT, SPEED_MEDIUM) + hass.states.async_set(_OSC_INPUT, 'True') + hass.states.async_set(_DIRECTION_INPUT_SELECT, DIRECTION_FORWARD) + await hass.async_block_till_done() + + _verify(hass, STATE_ON, SPEED_MEDIUM, True, DIRECTION_FORWARD) + + +async def test_templates_with_valid_values(hass, calls): + """Test templates with valid values.""" + with assert_setup_component(1, 'fan'): + assert await setup.async_setup_component(hass, 'fan', { + 'fan': { + 'platform': 'template', + 'fans': { + 'test_fan': { + '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() + + _verify(hass, STATE_ON, SPEED_MEDIUM, True, DIRECTION_FORWARD) + + +async def test_templates_invalid_values(hass, calls): + """Test templates with invalid values.""" + with assert_setup_component(1, 'fan'): + assert await setup.async_setup_component(hass, 'fan', { + 'fan': { + 'platform': 'template', + 'fans': { + 'test_fan': { + 'value_template': + "{{ 'abc' }}", + 'speed_template': + "{{ '0' }}", + 'oscillating_template': + "{{ 'xyz' }}", + 'direction_template': + "{{ 'right' }}", + + 'turn_on': { + 'service': 'script.fan_on' + }, + 'turn_off': { + 'service': 'script.fan_off' + } + } + } + } + }) + + await hass.async_start() + await hass.async_block_till_done() + + _verify(hass, STATE_OFF, None, None, None) + +# End of template tests # + + +# Function tests # +async def test_on_off(hass, calls): + """Test turn on and turn off.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_STATE_INPUT_BOOLEAN).state == STATE_ON + _verify(hass, STATE_ON, None, None, None) + + # Turn off fan + common.async_turn_off(hass, _TEST_FAN) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_STATE_INPUT_BOOLEAN).state == STATE_OFF + _verify(hass, STATE_OFF, None, None, None) + + +async def test_on_with_speed(hass, calls): + """Test turn on with speed.""" + await _register_components(hass) + + # Turn on fan with high speed + common.async_turn_on(hass, _TEST_FAN, SPEED_HIGH) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_STATE_INPUT_BOOLEAN).state == STATE_ON + assert hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_HIGH + _verify(hass, STATE_ON, SPEED_HIGH, None, None) + + +async def test_set_speed(hass, calls): + """Test set valid speed.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's speed to high + common.async_set_speed(hass, _TEST_FAN, SPEED_HIGH) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_HIGH + _verify(hass, STATE_ON, SPEED_HIGH, None, None) + + # Set fan's speed to medium + common.async_set_speed(hass, _TEST_FAN, SPEED_MEDIUM) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_MEDIUM + _verify(hass, STATE_ON, SPEED_MEDIUM, None, None) + + +async def test_set_invalid_speed_from_initial_stage(hass, calls): + """Test set invalid speed when fan is in initial state.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's speed to 'invalid' + common.async_set_speed(hass, _TEST_FAN, 'invalid') + await hass.async_block_till_done() + + # verify speed is unchanged + assert hass.states.get(_SPEED_INPUT_SELECT).state == '' + _verify(hass, STATE_ON, None, None, None) + + +async def test_set_invalid_speed(hass, calls): + """Test set invalid speed when fan has valid speed.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's speed to high + common.async_set_speed(hass, _TEST_FAN, SPEED_HIGH) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_HIGH + _verify(hass, STATE_ON, SPEED_HIGH, None, None) + + # Set fan's speed to 'invalid' + common.async_set_speed(hass, _TEST_FAN, 'invalid') + await hass.async_block_till_done() + + # verify speed is unchanged + assert hass.states.get(_SPEED_INPUT_SELECT).state == SPEED_HIGH + _verify(hass, STATE_ON, SPEED_HIGH, None, None) + + +async def test_custom_speed_list(hass, calls): + """Test set custom speed list.""" + await _register_components(hass, ['1', '2', '3']) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's speed to '1' + common.async_set_speed(hass, _TEST_FAN, '1') + await hass.async_block_till_done() + + # verify + assert hass.states.get(_SPEED_INPUT_SELECT).state == '1' + _verify(hass, STATE_ON, '1', None, None) + + # Set fan's speed to 'medium' which is invalid + common.async_set_speed(hass, _TEST_FAN, SPEED_MEDIUM) + await hass.async_block_till_done() + + # verify that speed is unchanged + assert hass.states.get(_SPEED_INPUT_SELECT).state == '1' + _verify(hass, STATE_ON, '1', None, None) + + +async def test_set_osc(hass, calls): + """Test set oscillating.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's osc to True + common.async_oscillate(hass, _TEST_FAN, True) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_OSC_INPUT).state == 'True' + _verify(hass, STATE_ON, None, True, None) + + # Set fan's osc to False + common.async_oscillate(hass, _TEST_FAN, False) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_OSC_INPUT).state == 'False' + _verify(hass, STATE_ON, None, False, None) + + +async def test_set_invalid_osc_from_initial_state(hass, calls): + """Test set invalid oscillating when fan is in initial state.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's osc to 'invalid' + common.async_oscillate(hass, _TEST_FAN, 'invalid') + await hass.async_block_till_done() + + # verify + assert hass.states.get(_OSC_INPUT).state == '' + _verify(hass, STATE_ON, None, None, None) + + +async def test_set_invalid_osc(hass, calls): + """Test set invalid oscillating when fan has valid osc.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's osc to True + common.async_oscillate(hass, _TEST_FAN, True) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_OSC_INPUT).state == 'True' + _verify(hass, STATE_ON, None, True, None) + + # Set fan's osc to False + common.async_oscillate(hass, _TEST_FAN, None) + await hass.async_block_till_done() + + # verify osc is unchanged + assert hass.states.get(_OSC_INPUT).state == 'True' + _verify(hass, STATE_ON, None, True, None) + + +async def test_set_direction(hass, calls): + """Test set valid direction.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's direction to forward + common.async_set_direction(hass, _TEST_FAN, DIRECTION_FORWARD) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_DIRECTION_INPUT_SELECT).state \ + == DIRECTION_FORWARD + _verify(hass, STATE_ON, None, None, DIRECTION_FORWARD) + + # Set fan's direction to reverse + common.async_set_direction(hass, _TEST_FAN, DIRECTION_REVERSE) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_DIRECTION_INPUT_SELECT).state \ + == DIRECTION_REVERSE + _verify(hass, STATE_ON, None, None, DIRECTION_REVERSE) + + +async def test_set_invalid_direction_from_initial_stage(hass, calls): + """Test set invalid direction when fan is in initial state.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's direction to 'invalid' + common.async_set_direction(hass, _TEST_FAN, 'invalid') + await hass.async_block_till_done() + + # verify direction is unchanged + assert hass.states.get(_DIRECTION_INPUT_SELECT).state == '' + _verify(hass, STATE_ON, None, None, None) + + +async def test_set_invalid_direction(hass, calls): + """Test set invalid direction when fan has valid direction.""" + await _register_components(hass) + + # Turn on fan + common.async_turn_on(hass, _TEST_FAN) + await hass.async_block_till_done() + + # Set fan's direction to forward + common.async_set_direction(hass, _TEST_FAN, DIRECTION_FORWARD) + await hass.async_block_till_done() + + # verify + assert hass.states.get(_DIRECTION_INPUT_SELECT).state == \ + DIRECTION_FORWARD + _verify(hass, STATE_ON, None, None, DIRECTION_FORWARD) + + # Set fan's direction to 'invalid' + common.async_set_direction(hass, _TEST_FAN, 'invalid') + await hass.async_block_till_done() + + # verify direction is unchanged + assert hass.states.get(_DIRECTION_INPUT_SELECT).state == \ + DIRECTION_FORWARD + _verify(hass, STATE_ON, None, None, DIRECTION_FORWARD) + + +def _verify(hass, expected_state, expected_speed, expected_oscillating, + expected_direction): + """Verify fan's state, speed and osc.""" + state = hass.states.get(_TEST_FAN) + attributes = state.attributes + assert state.state == expected_state + assert attributes.get(ATTR_SPEED, None) == expected_speed + assert attributes.get(ATTR_OSCILLATING, None) == expected_oscillating + assert attributes.get(ATTR_DIRECTION, None) == expected_direction + + +async def _register_components(hass, speed_list=None): + """Register basic components for testing.""" + with assert_setup_component(1, 'input_boolean'): + assert await setup.async_setup_component( + hass, + 'input_boolean', + {'input_boolean': {'state': None}} + ) + + with assert_setup_component(3, 'input_select'): + assert await setup.async_setup_component(hass, 'input_select', { + 'input_select': { + 'speed': { + 'name': 'Speed', + 'options': ['', SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH, + '1', '2', '3'] + }, + + 'osc': { + 'name': 'oscillating', + 'options': ['', 'True', 'False'] + }, + + 'direction': { + 'name': 'Direction', + 'options': ['', DIRECTION_FORWARD, DIRECTION_REVERSE] + }, + } + }) + + with assert_setup_component(1, 'fan'): + value_template = """ + {% if is_state('input_boolean.state', 'on') %} + {{ 'on' }} + {% else %} + {{ 'off' }} + {% endif %} + """ + + test_fan_config = { + 'value_template': value_template, + 'speed_template': + "{{ states('input_select.speed') }}", + 'oscillating_template': + "{{ states('input_select.osc') }}", + 'direction_template': + "{{ states('input_select.direction') }}", + + 'turn_on': { + 'service': 'input_boolean.turn_on', + 'entity_id': _STATE_INPUT_BOOLEAN + }, + 'turn_off': { + 'service': 'input_boolean.turn_off', + 'entity_id': _STATE_INPUT_BOOLEAN + }, + 'set_speed': { + 'service': 'input_select.select_option', + + 'data_template': { + 'entity_id': _SPEED_INPUT_SELECT, + 'option': '{{ speed }}' + } + }, + 'set_oscillating': { + 'service': 'input_select.select_option', + + 'data_template': { + 'entity_id': _OSC_INPUT, + 'option': '{{ oscillating }}' + } + }, + 'set_direction': { + 'service': 'input_select.select_option', + + 'data_template': { + 'entity_id': _DIRECTION_INPUT_SELECT, + 'option': '{{ direction }}' + } + } + } + + if speed_list: + test_fan_config['speeds'] = speed_list + + assert await setup.async_setup_component(hass, 'fan', { + 'fan': { + 'platform': 'template', + 'fans': { + 'test_fan': test_fan_config + } + } + }) + + await hass.async_start() + await hass.async_block_till_done()