core/tests/components/tradfri/test_light.py

412 lines
12 KiB
Python
Raw Normal View History

"""Tradfri lights platform tests."""
from copy import deepcopy
from unittest.mock import MagicMock, Mock, PropertyMock, patch
import pytest
from pytradfri.device import Device
from pytradfri.device.light import Light
from pytradfri.device.light_control import LightControl
from homeassistant.components import tradfri
from tests.common import MockConfigEntry
2019-07-31 19:25:30 +00:00
DEFAULT_TEST_FEATURES = {
"can_set_dimmer": False,
"can_set_color": False,
"can_set_temp": False,
}
# [
# {bulb features},
# {turn_on arguments},
# {expected result}
# ]
TURN_ON_TEST_CASES = [
# Turn On
2019-07-31 19:25:30 +00:00
[{}, {}, {"state": "on"}],
# Brightness > 0
2019-07-31 19:25:30 +00:00
[{"can_set_dimmer": True}, {"brightness": 100}, {"state": "on", "brightness": 100}],
# Brightness == 1
2019-07-31 19:25:30 +00:00
[{"can_set_dimmer": True}, {"brightness": 1}, {"brightness": 1}],
# Brightness > 254
2019-07-31 19:25:30 +00:00
[{"can_set_dimmer": True}, {"brightness": 1000}, {"brightness": 254}],
# color_temp
2019-07-31 19:25:30 +00:00
[{"can_set_temp": True}, {"color_temp": 250}, {"color_temp": 250}],
# color_temp < 250
2019-07-31 19:25:30 +00:00
[{"can_set_temp": True}, {"color_temp": 1}, {"color_temp": 250}],
# color_temp > 454
2019-07-31 19:25:30 +00:00
[{"can_set_temp": True}, {"color_temp": 1000}, {"color_temp": 454}],
# hs color
[
2019-07-31 19:25:30 +00:00
{"can_set_color": True},
{"hs_color": [300, 100]},
{"state": "on", "hs_color": [300, 100]},
],
# ct + brightness
[
2019-07-31 19:25:30 +00:00
{"can_set_dimmer": True, "can_set_temp": True},
{"color_temp": 250, "brightness": 200},
{"state": "on", "color_temp": 250, "brightness": 200},
],
# ct + brightness (no temp support)
[
2019-07-31 19:25:30 +00:00
{"can_set_dimmer": True, "can_set_temp": False, "can_set_color": True},
{"color_temp": 250, "brightness": 200},
{"state": "on", "hs_color": [26.807, 34.869], "brightness": 200},
],
# ct + brightness (no temp or color support)
[
2019-07-31 19:25:30 +00:00
{"can_set_dimmer": True, "can_set_temp": False, "can_set_color": False},
{"color_temp": 250, "brightness": 200},
{"state": "on", "brightness": 200},
],
# hs + brightness
[
2019-07-31 19:25:30 +00:00
{"can_set_dimmer": True, "can_set_color": True},
{"hs_color": [300, 100], "brightness": 200},
{"state": "on", "hs_color": [300, 100], "brightness": 200},
],
]
# Result of transition is not tested, but data is passed to turn on service.
TRANSITION_CASES_FOR_TESTS = [None, 0, 1]
2019-07-31 19:25:30 +00:00
@pytest.fixture(autouse=True, scope="module")
def setup(request):
"""Set up patches for pytradfri methods."""
2019-07-31 19:25:30 +00:00
p_1 = patch(
"pytradfri.device.LightControl.raw",
new_callable=PropertyMock,
return_value=[{"mock": "mock"}],
)
p_2 = patch("pytradfri.device.LightControl.lights")
p_1.start()
p_2.start()
def teardown():
"""Remove patches for pytradfri methods."""
p_1.stop()
p_2.stop()
request.addfinalizer(teardown)
@pytest.fixture
def mock_gateway():
"""Mock a Tradfri gateway."""
2019-07-31 19:25:30 +00:00
def get_devices():
"""Return mock devices."""
return gateway.mock_devices
def get_groups():
"""Return mock groups."""
return gateway.mock_groups
gateway = Mock(
get_devices=get_devices,
get_groups=get_groups,
mock_devices=[],
mock_groups=[],
2019-07-31 19:25:30 +00:00
mock_responses=[],
)
return gateway
@pytest.fixture
def mock_api(mock_gateway):
"""Mock api."""
2019-07-31 19:25:30 +00:00
async def api(command):
"""Mock api function."""
# Store the data for "real" command objects.
2019-07-31 19:25:30 +00:00
if hasattr(command, "_data") and not isinstance(command, Mock):
mock_gateway.mock_responses.append(command._data)
return command
2019-07-31 19:25:30 +00:00
return api
async def generate_psk(self, code):
"""Mock psk."""
return "mock"
async def setup_gateway(hass, mock_gateway, mock_api):
"""Load the Tradfri platform with a mock gateway."""
2019-07-31 19:25:30 +00:00
entry = MockConfigEntry(
domain=tradfri.DOMAIN,
data={
"host": "mock-host",
"identity": "mock-identity",
"key": "mock-key",
"import_groups": True,
"gateway_id": "mock-gateway-id",
},
)
hass.data[tradfri.KEY_GATEWAY] = {entry.entry_id: mock_gateway}
hass.data[tradfri.KEY_API] = {entry.entry_id: mock_api}
2019-07-31 19:25:30 +00:00
await hass.config_entries.async_forward_entry_setup(entry, "light")
def mock_light(test_features={}, test_state={}, n=0):
"""Mock a tradfri light."""
2019-07-31 19:25:30 +00:00
mock_light_data = Mock(**test_state)
mock_light = Mock(
id=f"mock-light-id-{n}",
reachable=True,
observe=Mock(),
2019-07-31 19:25:30 +00:00
device_info=MagicMock(),
)
mock_light.name = f"tradfri_light_{n}"
# Set supported features for the light.
features = {**DEFAULT_TEST_FEATURES, **test_features}
lc = LightControl(mock_light)
for k, v in features.items():
setattr(lc, k, v)
# Store the initial state.
2019-07-31 19:25:30 +00:00
setattr(lc, "lights", [mock_light_data])
mock_light.light_control = lc
return mock_light
async def test_light(hass, mock_gateway, mock_api):
"""Test that lights are correctly added."""
2019-07-31 19:25:30 +00:00
features = {"can_set_dimmer": True, "can_set_color": True, "can_set_temp": True}
state = {
2019-07-31 19:25:30 +00:00
"state": True,
"dimmer": 100,
"color_temp": 250,
"hsb_xy_color": (100, 100, 100, 100, 100),
}
mock_gateway.mock_devices.append(
mock_light(test_features=features, test_state=state)
)
await setup_gateway(hass, mock_gateway, mock_api)
2019-07-31 19:25:30 +00:00
lamp_1 = hass.states.get("light.tradfri_light_0")
assert lamp_1 is not None
2019-07-31 19:25:30 +00:00
assert lamp_1.state == "on"
assert lamp_1.attributes["brightness"] == 100
assert lamp_1.attributes["hs_color"] == (0.549, 0.153)
async def test_light_observed(hass, mock_gateway, mock_api):
"""Test that lights are correctly observed."""
light = mock_light()
mock_gateway.mock_devices.append(light)
await setup_gateway(hass, mock_gateway, mock_api)
assert len(light.observe.mock_calls) > 0
async def test_light_available(hass, mock_gateway, mock_api):
"""Test light available property."""
2019-07-31 19:25:30 +00:00
light = mock_light({"state": True}, n=1)
light.reachable = True
2019-07-31 19:25:30 +00:00
light2 = mock_light({"state": True}, n=2)
light2.reachable = False
mock_gateway.mock_devices.append(light)
mock_gateway.mock_devices.append(light2)
await setup_gateway(hass, mock_gateway, mock_api)
2019-07-31 19:25:30 +00:00
assert hass.states.get("light.tradfri_light_1").state == "on"
2019-07-31 19:25:30 +00:00
assert hass.states.get("light.tradfri_light_2").state == "unavailable"
# Combine TURN_ON_TEST_CASES and TRANSITION_CASES_FOR_TESTS
2019-07-31 19:25:30 +00:00
ALL_TURN_ON_TEST_CASES = [["test_features", "test_data", "expected_result", "id"], []]
idx = 1
for tc in TURN_ON_TEST_CASES:
for trans in TRANSITION_CASES_FOR_TESTS:
case = deepcopy(tc)
if trans is not None:
2019-07-31 19:25:30 +00:00
case[1]["transition"] = trans
case.append(idx)
idx = idx + 1
ALL_TURN_ON_TEST_CASES[1].append(case)
@pytest.mark.parametrize(*ALL_TURN_ON_TEST_CASES)
2019-07-31 19:25:30 +00:00
async def test_turn_on(
hass, mock_gateway, mock_api, test_features, test_data, expected_result, id
):
"""Test turning on a light."""
# Note pytradfri style, not hass. Values not really important.
initial_state = {
2019-07-31 19:25:30 +00:00
"state": False,
"dimmer": 0,
"color_temp": 250,
"hsb_xy_color": (100, 100, 100, 100, 100),
}
# Setup the gateway with a mock light.
2019-07-31 19:25:30 +00:00
light = mock_light(test_features=test_features, test_state=initial_state, n=id)
mock_gateway.mock_devices.append(light)
await setup_gateway(hass, mock_gateway, mock_api)
# Use the turn_on service call to change the light state.
2019-07-31 19:25:30 +00:00
await hass.services.async_call(
"light",
"turn_on",
{"entity_id": f"light.tradfri_light_{id}", **test_data},
2019-07-31 19:25:30 +00:00
blocking=True,
)
await hass.async_block_till_done()
# Check that the light is observed.
mock_func = light.observe
assert len(mock_func.mock_calls) > 0
_, callkwargs = mock_func.call_args
2019-07-31 19:25:30 +00:00
assert "callback" in callkwargs
# Callback function to refresh light state.
2019-07-31 19:25:30 +00:00
cb = callkwargs["callback"]
responses = mock_gateway.mock_responses
# State on command data.
2019-07-31 19:25:30 +00:00
data = {"3311": [{"5850": 1}]}
# Add data for all sent commands.
for r in responses:
2019-07-31 19:25:30 +00:00
data["3311"][0] = {**data["3311"][0], **r["3311"][0]}
# Use the callback function to update the light state.
dev = Device(data)
light_data = Light(dev, 0)
light.light_control.lights[0] = light_data
cb(light)
await hass.async_block_till_done()
# Check that the state is correct.
states = hass.states.get(f"light.tradfri_light_{id}")
for k, v in expected_result.items():
2019-07-31 19:25:30 +00:00
if k == "state":
assert states.state == v
else:
# Allow some rounding error in color conversions.
assert states.attributes[k] == pytest.approx(v, abs=0.01)
async def test_turn_off(hass, mock_gateway, mock_api):
"""Test turning off a light."""
2019-07-31 19:25:30 +00:00
state = {"state": True, "dimmer": 100}
light = mock_light(test_state=state)
mock_gateway.mock_devices.append(light)
await setup_gateway(hass, mock_gateway, mock_api)
# Use the turn_off service call to change the light state.
2019-07-31 19:25:30 +00:00
await hass.services.async_call(
"light", "turn_off", {"entity_id": "light.tradfri_light_0"}, blocking=True
)
await hass.async_block_till_done()
# Check that the light is observed.
mock_func = light.observe
assert len(mock_func.mock_calls) > 0
_, callkwargs = mock_func.call_args
2019-07-31 19:25:30 +00:00
assert "callback" in callkwargs
# Callback function to refresh light state.
2019-07-31 19:25:30 +00:00
cb = callkwargs["callback"]
responses = mock_gateway.mock_responses
2019-07-31 19:25:30 +00:00
data = {"3311": [{}]}
# Add data for all sent commands.
for r in responses:
2019-07-31 19:25:30 +00:00
data["3311"][0] = {**data["3311"][0], **r["3311"][0]}
# Use the callback function to update the light state.
dev = Device(data)
light_data = Light(dev, 0)
light.light_control.lights[0] = light_data
cb(light)
await hass.async_block_till_done()
# Check that the state is correct.
2019-07-31 19:25:30 +00:00
states = hass.states.get("light.tradfri_light_0")
assert states.state == "off"
def mock_group(test_state={}, n=0):
"""Mock a Tradfri group."""
2019-07-31 19:25:30 +00:00
default_state = {"state": False, "dimmer": 0}
state = {**default_state, **test_state}
2019-07-31 19:25:30 +00:00
mock_group = Mock(member_ids=[], observe=Mock(), **state)
mock_group.name = f"tradfri_group_{n}"
return mock_group
async def test_group(hass, mock_gateway, mock_api):
"""Test that groups are correctly added."""
mock_gateway.mock_groups.append(mock_group())
2019-07-31 19:25:30 +00:00
state = {"state": True, "dimmer": 100}
mock_gateway.mock_groups.append(mock_group(state, 1))
await setup_gateway(hass, mock_gateway, mock_api)
2019-07-31 19:25:30 +00:00
group = hass.states.get("light.tradfri_group_0")
assert group is not None
2019-07-31 19:25:30 +00:00
assert group.state == "off"
2019-07-31 19:25:30 +00:00
group = hass.states.get("light.tradfri_group_1")
assert group is not None
2019-07-31 19:25:30 +00:00
assert group.state == "on"
assert group.attributes["brightness"] == 100
async def test_group_turn_on(hass, mock_gateway, mock_api):
"""Test turning on a group."""
group = mock_group()
group2 = mock_group(n=1)
group3 = mock_group(n=2)
mock_gateway.mock_groups.append(group)
mock_gateway.mock_groups.append(group2)
mock_gateway.mock_groups.append(group3)
await setup_gateway(hass, mock_gateway, mock_api)
# Use the turn_off service call to change the light state.
2019-07-31 19:25:30 +00:00
await hass.services.async_call(
"light", "turn_on", {"entity_id": "light.tradfri_group_0"}, blocking=True
)
await hass.services.async_call(
"light",
"turn_on",
{"entity_id": "light.tradfri_group_1", "brightness": 100},
blocking=True,
)
await hass.services.async_call(
"light",
"turn_on",
{"entity_id": "light.tradfri_group_2", "brightness": 100, "transition": 1},
blocking=True,
)
await hass.async_block_till_done()
group.set_state.assert_called_with(1)
group2.set_dimmer.assert_called_with(100)
group3.set_dimmer.assert_called_with(100, transition_time=10)
async def test_group_turn_off(hass, mock_gateway, mock_api):
"""Test turning off a group."""
2019-07-31 19:25:30 +00:00
group = mock_group({"state": True})
mock_gateway.mock_groups.append(group)
await setup_gateway(hass, mock_gateway, mock_api)
# Use the turn_off service call to change the light state.
2019-07-31 19:25:30 +00:00
await hass.services.async_call(
"light", "turn_off", {"entity_id": "light.tradfri_group_0"}, blocking=True
)
await hass.async_block_till_done()
group.set_state.assert_called_with(0)