"""deCONZ light platform tests.""" from copy import deepcopy from unittest.mock import patch from homeassistant.components.deconz.const import ( CONF_ALLOW_DECONZ_GROUPS, DOMAIN as DECONZ_DOMAIN, ) from homeassistant.components.deconz.gateway import get_gateway_from_config_entry from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, ATTR_HS_COLOR, ATTR_MAX_MIREDS, ATTR_MIN_MIREDS, ATTR_TRANSITION, DOMAIN as LIGHT_DOMAIN, EFFECT_COLORLOOP, FLASH_LONG, FLASH_SHORT, SERVICE_TURN_OFF, SERVICE_TURN_ON, ) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, STATE_OFF, STATE_ON, ) from homeassistant.setup import async_setup_component from .test_gateway import DECONZ_WEB_REQUEST, 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_platform_manually_configured(hass): """Test that we do not discover anything or try to set up a gateway.""" assert ( await async_setup_component( hass, LIGHT_DOMAIN, {"light": {"platform": DECONZ_DOMAIN}} ) is True ) assert DECONZ_DOMAIN not in hass.data async def test_no_lights_or_groups(hass): """Test that no lights or groups entities are created.""" await setup_deconz_integration(hass) assert len(hass.states.async_all()) == 0 async def test_lights_and_groups(hass): """Test that lights or groups entities are created.""" data = deepcopy(DECONZ_WEB_REQUEST) data["groups"] = deepcopy(GROUPS) data["lights"] = deepcopy(LIGHTS) config_entry = await setup_deconz_integration(hass, get_state_response=data) gateway = get_gateway_from_config_entry(hass, config_entry) assert len(hass.states.async_all()) == 6 rgb_light = hass.states.get("light.rgb_light") assert rgb_light.state == STATE_ON assert rgb_light.attributes[ATTR_BRIGHTNESS] == 255 assert rgb_light.attributes[ATTR_HS_COLOR] == (224.235, 100.0) assert rgb_light.attributes["is_deconz_group"] is False assert rgb_light.attributes[ATTR_SUPPORTED_FEATURES] == 61 tunable_white_light = hass.states.get("light.tunable_white_light") assert tunable_white_light.state == STATE_ON assert tunable_white_light.attributes[ATTR_COLOR_TEMP] == 2500 assert tunable_white_light.attributes[ATTR_MAX_MIREDS] == 454 assert tunable_white_light.attributes[ATTR_MIN_MIREDS] == 155 assert tunable_white_light.attributes[ATTR_SUPPORTED_FEATURES] == 2 tunable_white_light_bad_maxmin = hass.states.get( "light.tunable_white_light_with_bad_maxmin_values" ) assert tunable_white_light_bad_maxmin.state == STATE_ON assert tunable_white_light_bad_maxmin.attributes[ATTR_COLOR_TEMP] == 2500 assert tunable_white_light_bad_maxmin.attributes[ATTR_MAX_MIREDS] == 650 assert tunable_white_light_bad_maxmin.attributes[ATTR_MIN_MIREDS] == 140 assert tunable_white_light_bad_maxmin.attributes[ATTR_SUPPORTED_FEATURES] == 2 on_off_light = hass.states.get("light.on_off_light") assert on_off_light.state == STATE_ON assert on_off_light.attributes[ATTR_SUPPORTED_FEATURES] == 0 light_group = hass.states.get("light.light_group") assert light_group.state == STATE_ON assert light_group.attributes["all_on"] is False empty_group = hass.states.get("light.empty_group") assert empty_group is None state_changed_event = { "t": "event", "e": "changed", "r": "lights", "id": "1", "state": {"on": False}, } gateway.api.event_handler(state_changed_event) await hass.async_block_till_done() rgb_light = hass.states.get("light.rgb_light") assert rgb_light.state == STATE_OFF # Verify service calls rgb_light_device = gateway.api.lights["1"] # Service turn on light with short color loop with patch.object(rgb_light_device, "_request", return_value=True) as set_callback: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: "light.rgb_light", ATTR_COLOR_TEMP: 2500, ATTR_BRIGHTNESS: 200, ATTR_TRANSITION: 5, ATTR_FLASH: FLASH_SHORT, ATTR_EFFECT: EFFECT_COLORLOOP, }, blocking=True, ) await hass.async_block_till_done() set_callback.assert_called_with( "put", "/lights/1/state", json={ "ct": 2500, "bri": 200, "transitiontime": 50, "alert": "select", "effect": "colorloop", }, ) # Service turn on light disabling color loop with long flashing with patch.object(rgb_light_device, "_request", return_value=True) as set_callback: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: "light.rgb_light", ATTR_HS_COLOR: (20, 30), ATTR_FLASH: FLASH_LONG, ATTR_EFFECT: "None", }, blocking=True, ) await hass.async_block_till_done() set_callback.assert_called_with( "put", "/lights/1/state", json={"xy": (0.411, 0.351), "alert": "lselect", "effect": "none"}, ) # Service turn on light with short flashing with patch.object(rgb_light_device, "_request", return_value=True) as set_callback: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_OFF, { ATTR_ENTITY_ID: "light.rgb_light", ATTR_TRANSITION: 5, ATTR_FLASH: FLASH_SHORT, }, blocking=True, ) await hass.async_block_till_done() assert not set_callback.called state_changed_event = { "t": "event", "e": "changed", "r": "lights", "id": "1", "state": {"on": True}, } gateway.api.event_handler(state_changed_event) await hass.async_block_till_done() # Service turn off light with short flashing with patch.object(rgb_light_device, "_request", return_value=True) as set_callback: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_OFF, { ATTR_ENTITY_ID: "light.rgb_light", ATTR_TRANSITION: 5, ATTR_FLASH: FLASH_SHORT, }, blocking=True, ) await hass.async_block_till_done() set_callback.assert_called_with( "put", "/lights/1/state", json={"bri": 0, "transitiontime": 50, "alert": "select"}, ) # Service turn off light with long flashing with patch.object(rgb_light_device, "_request", return_value=True) as set_callback: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: "light.rgb_light", ATTR_FLASH: FLASH_LONG}, blocking=True, ) await hass.async_block_till_done() set_callback.assert_called_with( "put", "/lights/1/state", json={"alert": "lselect"} ) await hass.config_entries.async_unload(config_entry.entry_id) assert len(hass.states.async_all()) == 0 async def test_disable_light_groups(hass): """Test disallowing light groups work.""" data = deepcopy(DECONZ_WEB_REQUEST) data["groups"] = deepcopy(GROUPS) data["lights"] = deepcopy(LIGHTS) config_entry = await setup_deconz_integration( hass, options={CONF_ALLOW_DECONZ_GROUPS: False}, get_state_response=data, ) assert len(hass.states.async_all()) == 5 assert hass.states.get("light.rgb_light") assert hass.states.get("light.tunable_white_light") assert hass.states.get("light.light_group") is None assert hass.states.get("light.empty_group") is None hass.config_entries.async_update_entry( config_entry, options={CONF_ALLOW_DECONZ_GROUPS: True} ) await hass.async_block_till_done() assert len(hass.states.async_all()) == 6 assert hass.states.get("light.light_group") hass.config_entries.async_update_entry( config_entry, options={CONF_ALLOW_DECONZ_GROUPS: False} ) await hass.async_block_till_done() assert len(hass.states.async_all()) == 5 assert hass.states.get("light.light_group") is None async def test_configuration_tool(hass): """Test that lights or groups entities are created.""" data = deepcopy(DECONZ_WEB_REQUEST) data["lights"] = { "0": { "etag": "26839cb118f5bf7ba1f2108256644010", "hascolor": False, "lastannounced": None, "lastseen": "2020-11-22T11:27Z", "manufacturername": "dresden elektronik", "modelid": "ConBee II", "name": "Configuration tool 1", "state": {"reachable": True}, "swversion": "0x264a0700", "type": "Configuration tool", "uniqueid": "00:21:2e:ff:ff:05:a7:a3-01", } } await setup_deconz_integration(hass, get_state_response=data) assert len(hass.states.async_all()) == 0 async def test_lidl_christmas_light(hass): """Test that lights or groups entities are created.""" data = deepcopy(DECONZ_WEB_REQUEST) data["lights"] = { "0": { "etag": "87a89542bf9b9d0aa8134919056844f8", "hascolor": True, "lastannounced": None, "lastseen": "2020-12-05T22:57Z", "manufacturername": "_TZE200_s8gkrkxk", "modelid": "TS0601", "name": "xmas light", "state": { "bri": 25, "colormode": "hs", "effect": "none", "hue": 53691, "on": True, "reachable": True, "sat": 141, }, "swversion": None, "type": "Color dimmable light", "uniqueid": "58:8e:81:ff:fe:db:7b:be-01", } } config_entry = await setup_deconz_integration(hass, get_state_response=data) gateway = get_gateway_from_config_entry(hass, config_entry) xmas_light_device = gateway.api.lights["0"] assert len(hass.states.async_all()) == 1 with patch.object(xmas_light_device, "_request", return_value=True) as set_callback: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: "light.xmas_light", ATTR_HS_COLOR: (20, 30), }, blocking=True, ) await hass.async_block_till_done() set_callback.assert_called_with( "put", "/lights/0/state", json={"on": True, "hue": 3640, "sat": 76}, ) assert hass.states.get("light.xmas_light")