Use websocket fixture in deCONZ light tests (#47826)

Localize test data
Improve asserts#
This commit is contained in:
Robert Svensson 2021-03-18 11:01:32 +01:00 committed by GitHub
parent 7350215b4e
commit 3f2d3bd1b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,11 +1,10 @@
"""deCONZ light platform tests.""" """deCONZ light platform tests."""
from copy import deepcopy from unittest.mock import patch
import pytest import pytest
from homeassistant.components.deconz.const import CONF_ALLOW_DECONZ_GROUPS from homeassistant.components.deconz.const import CONF_ALLOW_DECONZ_GROUPS
from homeassistant.components.deconz.gateway import get_gateway_from_config_entry
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP, ATTR_COLOR_TEMP,
@ -36,75 +35,6 @@ from .test_gateway import (
setup_deconz_integration, setup_deconz_integration,
) )
GROUPS = {
"1": {
"id": "Light group id",
"name": "Light group",
"type": "LightGroup",
"state": {"all_on": False, "any_on": True},
"action": {},
"scenes": [],
"lights": ["1", "2"],
},
"2": {
"id": "Empty group id",
"name": "Empty group",
"type": "LightGroup",
"state": {},
"action": {},
"scenes": [],
"lights": [],
},
}
LIGHTS = {
"1": {
"id": "RGB light id",
"name": "RGB light",
"state": {
"on": True,
"bri": 255,
"colormode": "xy",
"effect": "colorloop",
"xy": (500, 500),
"reachable": True,
},
"type": "Extended color light",
"uniqueid": "00:00:00:00:00:00:00:00-00",
},
"2": {
"ctmax": 454,
"ctmin": 155,
"id": "Tunable white light id",
"name": "Tunable white light",
"state": {"on": True, "colormode": "ct", "ct": 2500, "reachable": True},
"type": "Tunable white light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
},
"3": {
"id": "On off switch id",
"name": "On off switch",
"type": "On/Off plug-in unit",
"state": {"reachable": True},
"uniqueid": "00:00:00:00:00:00:00:02-00",
},
"4": {
"name": "On off light",
"state": {"on": True, "reachable": True},
"type": "On and Off light",
"uniqueid": "00:00:00:00:00:00:00:03-00",
},
"5": {
"ctmax": 1000,
"ctmin": 0,
"id": "Tunable white light with bad maxmin values id",
"name": "Tunable white light with bad maxmin values",
"state": {"on": True, "colormode": "ct", "ct": 2500, "reachable": True},
"type": "Tunable white light",
"uniqueid": "00:00:00:00:00:00:00:04-00",
},
}
async def test_no_lights_or_groups(hass, aioclient_mock): async def test_no_lights_or_groups(hass, aioclient_mock):
"""Test that no lights or groups entities are created.""" """Test that no lights or groups entities are created."""
@ -112,15 +42,75 @@ async def test_no_lights_or_groups(hass, aioclient_mock):
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
async def test_lights_and_groups(hass, aioclient_mock): async def test_lights_and_groups(hass, aioclient_mock, mock_deconz_websocket):
"""Test that lights or groups entities are created.""" """Test that lights or groups entities are created."""
data = deepcopy(DECONZ_WEB_REQUEST) data = {
data["groups"] = deepcopy(GROUPS) "groups": {
data["lights"] = deepcopy(LIGHTS) "1": {
config_entry = await setup_deconz_integration( "id": "Light group id",
hass, aioclient_mock, get_state_response=data "name": "Light group",
) "type": "LightGroup",
gateway = get_gateway_from_config_entry(hass, config_entry) "state": {"all_on": False, "any_on": True},
"action": {},
"scenes": [],
"lights": ["1", "2"],
},
"2": {
"id": "Empty group id",
"name": "Empty group",
"type": "LightGroup",
"state": {},
"action": {},
"scenes": [],
"lights": [],
},
},
"lights": {
"1": {
"name": "RGB light",
"state": {
"on": True,
"bri": 255,
"colormode": "xy",
"effect": "colorloop",
"xy": (500, 500),
"reachable": True,
},
"type": "Extended color light",
"uniqueid": "00:00:00:00:00:00:00:00-00",
},
"2": {
"ctmax": 454,
"ctmin": 155,
"name": "Tunable white light",
"state": {"on": True, "colormode": "ct", "ct": 2500, "reachable": True},
"type": "Tunable white light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
},
"3": {
"name": "On off switch",
"type": "On/Off plug-in unit",
"state": {"reachable": True},
"uniqueid": "00:00:00:00:00:00:00:02-00",
},
"4": {
"name": "On off light",
"state": {"on": True, "reachable": True},
"type": "On and Off light",
"uniqueid": "00:00:00:00:00:00:00:03-00",
},
"5": {
"ctmax": 1000,
"ctmin": 0,
"name": "Tunable white light with bad maxmin values",
"state": {"on": True, "colormode": "ct", "ct": 2500, "reachable": True},
"type": "Tunable white light",
"uniqueid": "00:00:00:00:00:00:00:04-00",
},
},
}
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(hass, aioclient_mock)
assert len(hass.states.async_all()) == 6 assert len(hass.states.async_all()) == 6
@ -151,25 +141,23 @@ async def test_lights_and_groups(hass, aioclient_mock):
assert on_off_light.state == STATE_ON assert on_off_light.state == STATE_ON
assert on_off_light.attributes[ATTR_SUPPORTED_FEATURES] == 0 assert on_off_light.attributes[ATTR_SUPPORTED_FEATURES] == 0
light_group = hass.states.get("light.light_group") assert hass.states.get("light.light_group").state == STATE_ON
assert light_group.state == STATE_ON assert hass.states.get("light.light_group").attributes["all_on"] is False
assert light_group.attributes["all_on"] is False
empty_group = hass.states.get("light.empty_group") empty_group = hass.states.get("light.empty_group")
assert empty_group is None assert empty_group is None
state_changed_event = { event_changed_light = {
"t": "event", "t": "event",
"e": "changed", "e": "changed",
"r": "lights", "r": "lights",
"id": "1", "id": "1",
"state": {"on": False}, "state": {"on": False},
} }
gateway.api.event_handler(state_changed_event) await mock_deconz_websocket(data=event_changed_light)
await hass.async_block_till_done() await hass.async_block_till_done()
rgb_light = hass.states.get("light.rgb_light") assert hass.states.get("light.rgb_light").state == STATE_OFF
assert rgb_light.state == STATE_OFF
# Verify service calls # Verify service calls
@ -231,14 +219,14 @@ async def test_lights_and_groups(hass, aioclient_mock):
) )
assert len(aioclient_mock.mock_calls) == 3 # Not called assert len(aioclient_mock.mock_calls) == 3 # Not called
state_changed_event = { event_changed_light = {
"t": "event", "t": "event",
"e": "changed", "e": "changed",
"r": "lights", "r": "lights",
"id": "1", "id": "1",
"state": {"on": True}, "state": {"on": True},
} }
gateway.api.event_handler(state_changed_event) await mock_deconz_websocket(data=event_changed_light)
await hass.async_block_till_done() await hass.async_block_till_done()
# Service turn off light with short flashing # Service turn off light with short flashing
@ -272,7 +260,7 @@ async def test_lights_and_groups(hass, aioclient_mock):
await hass.config_entries.async_unload(config_entry.entry_id) await hass.config_entries.async_unload(config_entry.entry_id)
states = hass.states.async_all() states = hass.states.async_all()
assert len(hass.states.async_all()) == 6 assert len(states) == 6
for state in states: for state in states:
assert state.state == STATE_UNAVAILABLE assert state.state == STATE_UNAVAILABLE
@ -283,28 +271,56 @@ async def test_lights_and_groups(hass, aioclient_mock):
async def test_disable_light_groups(hass, aioclient_mock): async def test_disable_light_groups(hass, aioclient_mock):
"""Test disallowing light groups work.""" """Test disallowing light groups work."""
data = deepcopy(DECONZ_WEB_REQUEST) data = {
data["groups"] = deepcopy(GROUPS) "groups": {
data["lights"] = deepcopy(LIGHTS) "1": {
config_entry = await setup_deconz_integration( "id": "Light group id",
hass, "name": "Light group",
aioclient_mock, "type": "LightGroup",
options={CONF_ALLOW_DECONZ_GROUPS: False}, "state": {"all_on": False, "any_on": True},
get_state_response=data, "action": {},
) "scenes": [],
"lights": ["1"],
},
"2": {
"id": "Empty group id",
"name": "Empty group",
"type": "LightGroup",
"state": {},
"action": {},
"scenes": [],
"lights": [],
},
},
"lights": {
"1": {
"ctmax": 454,
"ctmin": 155,
"name": "Tunable white light",
"state": {"on": True, "colormode": "ct", "ct": 2500, "reachable": True},
"type": "Tunable white light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
},
},
}
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(
hass,
aioclient_mock,
options={CONF_ALLOW_DECONZ_GROUPS: False},
)
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 1
assert hass.states.get("light.rgb_light")
assert hass.states.get("light.tunable_white_light") assert hass.states.get("light.tunable_white_light")
assert hass.states.get("light.light_group") is None assert not hass.states.get("light.light_group")
assert hass.states.get("light.empty_group") is None assert not hass.states.get("light.empty_group")
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, options={CONF_ALLOW_DECONZ_GROUPS: True} config_entry, options={CONF_ALLOW_DECONZ_GROUPS: True}
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(hass.states.async_all()) == 6 assert len(hass.states.async_all()) == 2
assert hass.states.get("light.light_group") assert hass.states.get("light.light_group")
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
@ -312,62 +328,96 @@ async def test_disable_light_groups(hass, aioclient_mock):
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 1
assert hass.states.get("light.light_group") is None assert not hass.states.get("light.light_group")
async def test_configuration_tool(hass, aioclient_mock): async def test_configuration_tool(hass, aioclient_mock):
"""Test that lights or groups entities are created.""" """Test that configuration tool is not created."""
data = deepcopy(DECONZ_WEB_REQUEST) data = {
data["lights"] = { "lights": {
"0": { "0": {
"etag": "26839cb118f5bf7ba1f2108256644010", "etag": "26839cb118f5bf7ba1f2108256644010",
"hascolor": False, "hascolor": False,
"lastannounced": None, "lastannounced": None,
"lastseen": "2020-11-22T11:27Z", "lastseen": "2020-11-22T11:27Z",
"manufacturername": "dresden elektronik", "manufacturername": "dresden elektronik",
"modelid": "ConBee II", "modelid": "ConBee II",
"name": "Configuration tool 1", "name": "Configuration tool 1",
"state": {"reachable": True}, "state": {"reachable": True},
"swversion": "0x264a0700", "swversion": "0x264a0700",
"type": "Configuration tool", "type": "Configuration tool",
"uniqueid": "00:21:2e:ff:ff:05:a7:a3-01", "uniqueid": "00:21:2e:ff:ff:05:a7:a3-01",
}
} }
} }
await setup_deconz_integration(hass, aioclient_mock, get_state_response=data) with patch.dict(DECONZ_WEB_REQUEST, data):
await setup_deconz_integration(hass, aioclient_mock)
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
async def test_ikea_default_transition_time(hass, aioclient_mock):
"""Verify that service calls to IKEA lights always extend with transition tinme 0 if absent."""
data = {
"lights": {
"1": {
"manufacturername": "IKEA",
"name": "Dimmable light",
"state": {"on": True, "bri": 255, "reachable": True},
"type": "Dimmable light",
"uniqueid": "00:00:00:00:00:00:00:01-00",
},
},
}
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(hass, aioclient_mock)
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/1/state")
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "light.dimmable_light", ATTR_BRIGHTNESS: 100},
blocking=True,
)
assert aioclient_mock.mock_calls[1][2] == {
"bri": 100,
"on": True,
"transitiontime": 0,
}
async def test_lidl_christmas_light(hass, aioclient_mock): async def test_lidl_christmas_light(hass, aioclient_mock):
"""Test that lights or groups entities are created.""" """Test that lights or groups entities are created."""
data = deepcopy(DECONZ_WEB_REQUEST) data = {
data["lights"] = { "lights": {
"0": { "0": {
"etag": "87a89542bf9b9d0aa8134919056844f8", "etag": "87a89542bf9b9d0aa8134919056844f8",
"hascolor": True, "hascolor": True,
"lastannounced": None, "lastannounced": None,
"lastseen": "2020-12-05T22:57Z", "lastseen": "2020-12-05T22:57Z",
"manufacturername": "_TZE200_s8gkrkxk", "manufacturername": "_TZE200_s8gkrkxk",
"modelid": "TS0601", "modelid": "TS0601",
"name": "xmas light", "name": "xmas light",
"state": { "state": {
"bri": 25, "bri": 25,
"colormode": "hs", "colormode": "hs",
"effect": "none", "effect": "none",
"hue": 53691, "hue": 53691,
"on": True, "on": True,
"reachable": True, "reachable": True,
"sat": 141, "sat": 141,
}, },
"swversion": None, "swversion": None,
"type": "Color dimmable light", "type": "Color dimmable light",
"uniqueid": "58:8e:81:ff:fe:db:7b:be-01", "uniqueid": "58:8e:81:ff:fe:db:7b:be-01",
}
} }
} }
config_entry = await setup_deconz_integration(
hass, aioclient_mock, get_state_response=data with patch.dict(DECONZ_WEB_REQUEST, data):
) config_entry = await setup_deconz_integration(hass, aioclient_mock)
mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/0/state") mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/0/state")
@ -385,100 +435,98 @@ async def test_lidl_christmas_light(hass, aioclient_mock):
assert hass.states.get("light.xmas_light") assert hass.states.get("light.xmas_light")
async def test_non_color_light_reports_color(hass, aioclient_mock): async def test_non_color_light_reports_color(
hass, aioclient_mock, mock_deconz_websocket
):
"""Verify hs_color does not crash when a group gets updated with a bad color value. """Verify hs_color does not crash when a group gets updated with a bad color value.
After calling a scene color temp light of certain manufacturers After calling a scene color temp light of certain manufacturers
report color temp in color space. report color temp in color space.
""" """
data = deepcopy(DECONZ_WEB_REQUEST) data = {
"groups": {
data["groups"] = { "0": {
"0": { "action": {
"action": { "alert": "none",
"alert": "none", "bri": 127,
"bri": 127, "colormode": "hs",
"colormode": "hs", "ct": 0,
"ct": 0, "effect": "none",
"effect": "none", "hue": 0,
"hue": 0, "on": True,
"on": True, "sat": 127,
"sat": 127, "scene": None,
"scene": None, "xy": [0, 0],
"xy": [0, 0], },
}, "devicemembership": [],
"devicemembership": [], "etag": "81e42cf1b47affb72fa72bc2e25ba8bf",
"etag": "81e42cf1b47affb72fa72bc2e25ba8bf", "lights": ["0", "1"],
"id": "0", "name": "All",
"lights": ["0", "1"], "scenes": [],
"name": "All", "state": {"all_on": False, "any_on": True},
"scenes": [], "type": "LightGroup",
"state": {"all_on": False, "any_on": True}, }
"type": "LightGroup", },
} "lights": {
} "0": {
"ctmax": 500,
data["lights"] = { "ctmin": 153,
"0": { "etag": "026bcfe544ad76c7534e5ca8ed39047c",
"ctmax": 500,
"ctmin": 153,
"etag": "026bcfe544ad76c7534e5ca8ed39047c",
"hascolor": True,
"manufacturername": "dresden elektronik",
"modelid": "FLS-PP3",
"name": "Light 1",
"pointsymbol": {},
"state": {
"alert": None,
"bri": 111,
"colormode": "ct",
"ct": 307,
"effect": None,
"hascolor": True, "hascolor": True,
"hue": 7998, "manufacturername": "dresden elektronik",
"on": False, "modelid": "FLS-PP3",
"reachable": True, "name": "Light 1",
"sat": 172, "pointsymbol": {},
"xy": [0.421253, 0.39921], "state": {
"alert": None,
"bri": 111,
"colormode": "ct",
"ct": 307,
"effect": None,
"hascolor": True,
"hue": 7998,
"on": False,
"reachable": True,
"sat": 172,
"xy": [0.421253, 0.39921],
},
"swversion": "020C.201000A0",
"type": "Extended color light",
"uniqueid": "00:21:2E:FF:FF:EE:DD:CC-0A",
}, },
"swversion": "020C.201000A0", "1": {
"type": "Extended color light", "colorcapabilities": 0,
"uniqueid": "00:21:2E:FF:FF:EE:DD:CC-0A", "ctmax": 65535,
}, "ctmin": 0,
"1": { "etag": "9dd510cd474791481f189d2a68a3c7f1",
"colorcapabilities": 0, "hascolor": True,
"ctmax": 65535, "lastannounced": "2020-12-17T17:44:38Z",
"ctmin": 0, "lastseen": "2021-01-11T18:36Z",
"etag": "9dd510cd474791481f189d2a68a3c7f1", "manufacturername": "IKEA of Sweden",
"hascolor": True, "modelid": "TRADFRI bulb E27 WS opal 1000lm",
"lastannounced": "2020-12-17T17:44:38Z", "name": "Küchenlicht",
"lastseen": "2021-01-11T18:36Z", "state": {
"manufacturername": "IKEA of Sweden", "alert": "none",
"modelid": "TRADFRI bulb E27 WS opal 1000lm", "bri": 156,
"name": "chenlicht", "colormode": "ct",
"state": { "ct": 250,
"alert": "none", "on": True,
"bri": 156, "reachable": True,
"colormode": "ct", },
"ct": 250, "swversion": "2.0.022",
"on": True, "type": "Color temperature light",
"reachable": True, "uniqueid": "ec:1b:bd:ff:fe:ee:ed:dd-01",
}, },
"swversion": "2.0.022",
"type": "Color temperature light",
"uniqueid": "ec:1b:bd:ff:fe:ee:ed:dd-01",
}, },
} }
config_entry = await setup_deconz_integration( with patch.dict(DECONZ_WEB_REQUEST, data):
hass, aioclient_mock, get_state_response=data await setup_deconz_integration(hass, aioclient_mock)
)
gateway = get_gateway_from_config_entry(hass, config_entry)
assert len(hass.states.async_all()) == 3 assert len(hass.states.async_all()) == 3
assert hass.states.get("light.all").attributes[ATTR_COLOR_TEMP] == 307 assert hass.states.get("light.all").attributes[ATTR_COLOR_TEMP] == 307
# Updating a scene will return a faulty color value for a non-color light causing an exception in hs_color # Updating a scene will return a faulty color value for a non-color light causing an exception in hs_color
state_changed_event = { event_changed_light = {
"e": "changed", "e": "changed",
"id": "1", "id": "1",
"r": "lights", "r": "lights",
@ -493,7 +541,7 @@ async def test_non_color_light_reports_color(hass, aioclient_mock):
"t": "event", "t": "event",
"uniqueid": "ec:1b:bd:ff:fe:ee:ed:dd-01", "uniqueid": "ec:1b:bd:ff:fe:ee:ed:dd-01",
} }
gateway.api.event_handler(state_changed_event) await mock_deconz_websocket(data=event_changed_light)
await hass.async_block_till_done() await hass.async_block_till_done()
# Bug is fixed if we reach this point, but device won't have neither color temp nor color # Bug is fixed if we reach this point, but device won't have neither color temp nor color
@ -504,9 +552,8 @@ async def test_non_color_light_reports_color(hass, aioclient_mock):
async def test_verify_group_supported_features(hass, aioclient_mock): async def test_verify_group_supported_features(hass, aioclient_mock):
"""Test that group supported features reflect what included lights support.""" """Test that group supported features reflect what included lights support."""
data = deepcopy(DECONZ_WEB_REQUEST) data = {
data["groups"] = deepcopy( "groups": {
{
"1": { "1": {
"id": "Group1", "id": "Group1",
"name": "group", "name": "group",
@ -516,19 +563,15 @@ async def test_verify_group_supported_features(hass, aioclient_mock):
"scenes": [], "scenes": [],
"lights": ["1", "2", "3"], "lights": ["1", "2", "3"],
}, },
} },
) "lights": {
data["lights"] = deepcopy(
{
"1": { "1": {
"id": "light1",
"name": "Dimmable light", "name": "Dimmable light",
"state": {"on": True, "bri": 255, "reachable": True}, "state": {"on": True, "bri": 255, "reachable": True},
"type": "Light", "type": "Light",
"uniqueid": "00:00:00:00:00:00:00:01-00", "uniqueid": "00:00:00:00:00:00:00:01-00",
}, },
"2": { "2": {
"id": "light2",
"name": "Color light", "name": "Color light",
"state": { "state": {
"on": True, "on": True,
@ -544,18 +587,17 @@ async def test_verify_group_supported_features(hass, aioclient_mock):
"3": { "3": {
"ctmax": 454, "ctmax": 454,
"ctmin": 155, "ctmin": 155,
"id": "light3",
"name": "Tunable light", "name": "Tunable light",
"state": {"on": True, "colormode": "ct", "ct": 2500, "reachable": True}, "state": {"on": True, "colormode": "ct", "ct": 2500, "reachable": True},
"type": "Tunable white light", "type": "Tunable white light",
"uniqueid": "00:00:00:00:00:00:00:03-00", "uniqueid": "00:00:00:00:00:00:00:03-00",
}, },
} },
) }
await setup_deconz_integration(hass, aioclient_mock, get_state_response=data) with patch.dict(DECONZ_WEB_REQUEST, data):
await setup_deconz_integration(hass, aioclient_mock)
assert len(hass.states.async_all()) == 4 assert len(hass.states.async_all()) == 4
group = hass.states.get("light.group") assert hass.states.get("light.group").state == STATE_ON
assert group.state == STATE_ON assert hass.states.get("light.group").attributes[ATTR_SUPPORTED_FEATURES] == 63
assert group.attributes[ATTR_SUPPORTED_FEATURES] == 63