hass-core/tests/components/template/test_fan.py

623 lines
19 KiB
Python
Raw Normal View History

"""The tests for the Template fan platform."""
import logging
import pytest
import voluptuous as vol
from homeassistant import setup
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.components.fan import (
ATTR_SPEED, ATTR_OSCILLATING, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH,
ATTR_DIRECTION, DIRECTION_FORWARD, DIRECTION_REVERSE)
from tests.common import (
async_mock_service, assert_setup_component)
from tests.components.fan import common
_LOGGER = logging.getLogger(__name__)
_TEST_FAN = 'fan.test_fan'
# Represent for fan's state
_STATE_INPUT_BOOLEAN = 'input_boolean.state'
# Represent for fan's speed
_SPEED_INPUT_SELECT = 'input_select.speed'
# Represent for fan's oscillating
_OSC_INPUT = 'input_select.osc'
# Represent for fan's direction
_DIRECTION_INPUT_SELECT = 'input_select.direction'
@pytest.fixture
def calls(hass):
"""Track calls to a mock serivce."""
return async_mock_service(hass, 'test', 'automation')
# 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' }}",
'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, None, None, None)
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'
}
}
}
}
})
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.async_all() == []
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'
}
}
}
}
})
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.async_all() == []
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': {
'value_template': "{{ 'on' }}",
'turn_on': {
'service': 'script.fan_on'
}
}
}
}
})
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.async_all() == []
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'
}
}
}
})
await hass.async_start()
await hass.async_block_till_done()
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'
}
}
}
}
})
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
await common.async_turn_on(hass, _TEST_FAN)
# verify
assert hass.states.get(_STATE_INPUT_BOOLEAN).state == STATE_ON
_verify(hass, STATE_ON, None, None, None)
# Turn off fan
await common.async_turn_off(hass, _TEST_FAN)
# 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
await common.async_turn_on(hass, _TEST_FAN, SPEED_HIGH)
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's speed to high
await common.async_set_speed(hass, _TEST_FAN, SPEED_HIGH)
# 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
await common.async_set_speed(hass, _TEST_FAN, SPEED_MEDIUM)
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's speed to 'invalid'
await common.async_set_speed(hass, _TEST_FAN, 'invalid')
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's speed to high
await common.async_set_speed(hass, _TEST_FAN, SPEED_HIGH)
# 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'
await common.async_set_speed(hass, _TEST_FAN, 'invalid')
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's speed to '1'
await common.async_set_speed(hass, _TEST_FAN, '1')
# 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
await common.async_set_speed(hass, _TEST_FAN, SPEED_MEDIUM)
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's osc to True
await common.async_oscillate(hass, _TEST_FAN, True)
# verify
assert hass.states.get(_OSC_INPUT).state == 'True'
_verify(hass, STATE_ON, None, True, None)
# Set fan's osc to False
await common.async_oscillate(hass, _TEST_FAN, False)
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's osc to 'invalid'
with pytest.raises(vol.Invalid):
await common.async_oscillate(hass, _TEST_FAN, 'invalid')
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's osc to True
await common.async_oscillate(hass, _TEST_FAN, True)
# verify
assert hass.states.get(_OSC_INPUT).state == 'True'
_verify(hass, STATE_ON, None, True, None)
# Set fan's osc to None
with pytest.raises(vol.Invalid):
await common.async_oscillate(hass, _TEST_FAN, None)
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's direction to forward
await common.async_set_direction(hass, _TEST_FAN, DIRECTION_FORWARD)
# 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
await common.async_set_direction(hass, _TEST_FAN, DIRECTION_REVERSE)
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's direction to 'invalid'
await common.async_set_direction(hass, _TEST_FAN, 'invalid')
# 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
await common.async_turn_on(hass, _TEST_FAN)
# Set fan's direction to forward
await common.async_set_direction(hass, _TEST_FAN, DIRECTION_FORWARD)
# 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'
await common.async_set_direction(hass, _TEST_FAN, 'invalid')
# 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()