"""The tests for the Light component.""" from unittest.mock import MagicMock, mock_open, patch import pytest import voluptuous as vol from homeassistant import core from homeassistant.components import light from homeassistant.const import ( ATTR_ENTITY_ID, CONF_PLATFORM, ENTITY_MATCH_ALL, SERVICE_TOGGLE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, STATE_ON, ) from homeassistant.exceptions import Unauthorized from homeassistant.setup import async_setup_component import homeassistant.util.color as color_util from tests.common import async_mock_service orig_Profiles = light.Profiles async def test_methods(hass): """Test if methods call the services as expected.""" # Test is_on hass.states.async_set("light.test", STATE_ON) assert light.is_on(hass, "light.test") hass.states.async_set("light.test", STATE_OFF) assert not light.is_on(hass, "light.test") # Test turn_on turn_on_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: "entity_id_val", light.ATTR_TRANSITION: "transition_val", light.ATTR_BRIGHTNESS: "brightness_val", light.ATTR_RGB_COLOR: "rgb_color_val", light.ATTR_XY_COLOR: "xy_color_val", light.ATTR_PROFILE: "profile_val", light.ATTR_COLOR_NAME: "color_name_val", light.ATTR_WHITE_VALUE: "white_val", }, blocking=True, ) assert len(turn_on_calls) == 1 call = turn_on_calls[-1] assert call.domain == light.DOMAIN assert call.service == SERVICE_TURN_ON assert call.data.get(ATTR_ENTITY_ID) == "entity_id_val" assert call.data.get(light.ATTR_TRANSITION) == "transition_val" assert call.data.get(light.ATTR_BRIGHTNESS) == "brightness_val" assert call.data.get(light.ATTR_RGB_COLOR) == "rgb_color_val" assert call.data.get(light.ATTR_XY_COLOR) == "xy_color_val" assert call.data.get(light.ATTR_PROFILE) == "profile_val" assert call.data.get(light.ATTR_COLOR_NAME) == "color_name_val" assert call.data.get(light.ATTR_WHITE_VALUE) == "white_val" # Test turn_off turn_off_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_OFF) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_OFF, { ATTR_ENTITY_ID: "entity_id_val", light.ATTR_TRANSITION: "transition_val", }, blocking=True, ) assert len(turn_off_calls) == 1 call = turn_off_calls[-1] assert call.domain == light.DOMAIN assert call.service == SERVICE_TURN_OFF assert call.data[ATTR_ENTITY_ID] == "entity_id_val" assert call.data[light.ATTR_TRANSITION] == "transition_val" # Test toggle toggle_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TOGGLE) await hass.services.async_call( light.DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: "entity_id_val", light.ATTR_TRANSITION: "transition_val"}, blocking=True, ) assert len(toggle_calls) == 1 call = toggle_calls[-1] assert call.domain == light.DOMAIN assert call.service == SERVICE_TOGGLE assert call.data[ATTR_ENTITY_ID] == "entity_id_val" assert call.data[light.ATTR_TRANSITION] == "transition_val" async def test_services(hass, mock_light_profiles, enable_custom_integrations): """Test the provided services.""" platform = getattr(hass.components, "test.light") platform.init() assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} ) await hass.async_block_till_done() ent1, ent2, ent3 = platform.ENTITIES ent1.supported_color_modes = [light.COLOR_MODE_HS] ent3.supported_color_modes = [light.COLOR_MODE_HS] ent1.supported_features = light.SUPPORT_TRANSITION ent2.supported_features = ( light.SUPPORT_COLOR | light.SUPPORT_EFFECT | light.SUPPORT_TRANSITION | light.SUPPORT_WHITE_VALUE ) ent3.supported_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION # Test init assert light.is_on(hass, ent1.entity_id) assert not light.is_on(hass, ent2.entity_id) assert not light.is_on(hass, ent3.entity_id) # Test basic turn_on, turn_off, toggle services await hass.services.async_call( light.DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ent1.entity_id}, blocking=True ) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent2.entity_id}, blocking=True ) assert not light.is_on(hass, ent1.entity_id) assert light.is_on(hass, ent2.entity_id) # turn on all lights await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True ) assert light.is_on(hass, ent1.entity_id) assert light.is_on(hass, ent2.entity_id) assert light.is_on(hass, ent3.entity_id) # turn off all lights await hass.services.async_call( light.DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True, ) assert not light.is_on(hass, ent1.entity_id) assert not light.is_on(hass, ent2.entity_id) assert not light.is_on(hass, ent3.entity_id) # turn off all lights by setting brightness to 0 await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True ) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL, light.ATTR_BRIGHTNESS: 0}, blocking=True, ) assert not light.is_on(hass, ent1.entity_id) assert not light.is_on(hass, ent2.entity_id) assert not light.is_on(hass, ent3.entity_id) # toggle all lights await hass.services.async_call( light.DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True ) assert light.is_on(hass, ent1.entity_id) assert light.is_on(hass, ent2.entity_id) assert light.is_on(hass, ent3.entity_id) # toggle all lights await hass.services.async_call( light.DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True ) assert not light.is_on(hass, ent1.entity_id) assert not light.is_on(hass, ent2.entity_id) assert not light.is_on(hass, ent3.entity_id) # Ensure all attributes process correctly await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_TRANSITION: 10, light.ATTR_BRIGHTNESS: 20, light.ATTR_COLOR_NAME: "blue", }, blocking=True, ) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent2.entity_id, light.ATTR_EFFECT: "fun_effect", light.ATTR_RGB_COLOR: (255, 255, 255), light.ATTR_WHITE_VALUE: 255, }, blocking=True, ) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent3.entity_id, light.ATTR_FLASH: "short", light.ATTR_XY_COLOR: (0.4, 0.6), }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == { light.ATTR_TRANSITION: 10, light.ATTR_BRIGHTNESS: 20, light.ATTR_HS_COLOR: (240, 100), } _, data = ent2.last_call("turn_on") assert data == { light.ATTR_EFFECT: "fun_effect", light.ATTR_HS_COLOR: (0, 0), light.ATTR_WHITE_VALUE: 255, } _, data = ent3.last_call("turn_on") assert data == {light.ATTR_FLASH: "short", light.ATTR_HS_COLOR: (71.059, 100)} # Ensure attributes are filtered when light is turned off await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_TRANSITION: 10, light.ATTR_BRIGHTNESS: 0, light.ATTR_COLOR_NAME: "blue", }, blocking=True, ) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent2.entity_id, light.ATTR_BRIGHTNESS: 0, light.ATTR_RGB_COLOR: (255, 255, 255), light.ATTR_WHITE_VALUE: 0, }, blocking=True, ) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent3.entity_id, light.ATTR_BRIGHTNESS: 0, light.ATTR_XY_COLOR: (0.4, 0.6), }, blocking=True, ) assert not light.is_on(hass, ent1.entity_id) assert not light.is_on(hass, ent2.entity_id) assert not light.is_on(hass, ent3.entity_id) _, data = ent1.last_call("turn_off") assert data == {light.ATTR_TRANSITION: 10} _, data = ent2.last_call("turn_off") assert data == {} _, data = ent3.last_call("turn_off") assert data == {} # One of the light profiles profile = light.Profile("relax", 0.513, 0.413, 144, 0) mock_light_profiles[profile.name] = profile # Test light profiles await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_PROFILE: profile.name}, blocking=True, ) # Specify a profile and a brightness attribute to overwrite it await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent2.entity_id, light.ATTR_PROFILE: profile.name, light.ATTR_BRIGHTNESS: 100, light.ATTR_TRANSITION: 1, }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == { light.ATTR_BRIGHTNESS: profile.brightness, light.ATTR_HS_COLOR: profile.hs_color, light.ATTR_TRANSITION: profile.transition, } _, data = ent2.last_call("turn_on") assert data == { light.ATTR_BRIGHTNESS: 100, light.ATTR_HS_COLOR: profile.hs_color, light.ATTR_TRANSITION: 1, } # Test toggle with parameters await hass.services.async_call( light.DOMAIN, SERVICE_TOGGLE, { ATTR_ENTITY_ID: ent3.entity_id, light.ATTR_PROFILE: profile.name, light.ATTR_BRIGHTNESS_PCT: 100, }, blocking=True, ) _, data = ent3.last_call("turn_on") assert data == { light.ATTR_BRIGHTNESS: 255, light.ATTR_HS_COLOR: profile.hs_color, light.ATTR_TRANSITION: profile.transition, } await hass.services.async_call( light.DOMAIN, SERVICE_TOGGLE, { ATTR_ENTITY_ID: ent3.entity_id, light.ATTR_TRANSITION: 4, }, blocking=True, ) _, data = ent3.last_call("turn_off") assert data == { light.ATTR_TRANSITION: 4, } # Test bad data await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True ) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_PROFILE: -1}, blocking=True, ) with pytest.raises(vol.MultipleInvalid): await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent2.entity_id, light.ATTR_XY_COLOR: ["bla-di-bla", 5]}, blocking=True, ) with pytest.raises(vol.MultipleInvalid): await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent3.entity_id, light.ATTR_RGB_COLOR: [255, None, 2]}, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} _, data = ent2.last_call("turn_on") assert data == {} _, data = ent3.last_call("turn_on") assert data == {} # faulty attributes will not trigger a service call with pytest.raises(vol.MultipleInvalid): await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_PROFILE: profile.name, light.ATTR_BRIGHTNESS: "bright", }, blocking=True, ) with pytest.raises(vol.MultipleInvalid): await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_RGB_COLOR: "yellowish", }, blocking=True, ) with pytest.raises(vol.MultipleInvalid): await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent2.entity_id, light.ATTR_WHITE_VALUE: "high"}, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} _, data = ent2.last_call("turn_on") assert data == {} @pytest.mark.parametrize( "profile_name, last_call, expected_data", ( ( "test", "turn_on", { light.ATTR_HS_COLOR: (71.059, 100), light.ATTR_BRIGHTNESS: 100, light.ATTR_TRANSITION: 0, }, ), ( "color_no_brightness_no_transition", "turn_on", { light.ATTR_HS_COLOR: (71.059, 100), }, ), ( "no color", "turn_on", { light.ATTR_BRIGHTNESS: 110, light.ATTR_TRANSITION: 0, }, ), ( "test_off", "turn_off", { light.ATTR_TRANSITION: 0, }, ), ( "no brightness", "turn_on", { light.ATTR_HS_COLOR: (71.059, 100), }, ), ( "color_and_brightness", "turn_on", { light.ATTR_HS_COLOR: (71.059, 100), light.ATTR_BRIGHTNESS: 120, }, ), ( "color_and_transition", "turn_on", { light.ATTR_HS_COLOR: (71.059, 100), light.ATTR_TRANSITION: 4.2, }, ), ( "brightness_and_transition", "turn_on", { light.ATTR_BRIGHTNESS: 130, light.ATTR_TRANSITION: 5.3, }, ), ), ) async def test_light_profiles( hass, mock_light_profiles, profile_name, expected_data, last_call, enable_custom_integrations, ): """Test light profiles.""" platform = getattr(hass.components, "test.light") platform.init() profile_mock_data = { "test": (0.4, 0.6, 100, 0), "color_no_brightness_no_transition": (0.4, 0.6, None, None), "no color": (None, None, 110, 0), "test_off": (0, 0, 0, 0), "no brightness": (0.4, 0.6, None), "color_and_brightness": (0.4, 0.6, 120), "color_and_transition": (0.4, 0.6, None, 4.2), "brightness_and_transition": (None, None, 130, 5.3), } for name, data in profile_mock_data.items(): mock_light_profiles[name] = light.Profile(*(name, *data)) assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} ) await hass.async_block_till_done() ent1, _, _ = platform.ENTITIES ent1.supported_color_modes = [light.COLOR_MODE_HS] ent1.supported_features = light.SUPPORT_TRANSITION await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_PROFILE: profile_name, }, blocking=True, ) _, data = ent1.last_call(last_call) if last_call == "turn_on": assert light.is_on(hass, ent1.entity_id) else: assert not light.is_on(hass, ent1.entity_id) assert data == expected_data async def test_default_profiles_group( hass, mock_light_profiles, enable_custom_integrations ): """Test default turn-on light profile for all lights.""" platform = getattr(hass.components, "test.light") platform.init() assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} ) await hass.async_block_till_done() profile = light.Profile("group.all_lights.default", 0.4, 0.6, 99, 2) mock_light_profiles[profile.name] = profile ent, _, _ = platform.ENTITIES ent.supported_color_modes = [light.COLOR_MODE_HS] ent.supported_features = light.SUPPORT_TRANSITION await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent.entity_id}, blocking=True ) _, data = ent.last_call("turn_on") assert data == { light.ATTR_HS_COLOR: (71.059, 100), light.ATTR_BRIGHTNESS: 99, light.ATTR_TRANSITION: 2, } @pytest.mark.parametrize( "extra_call_params, expected_params_state_was_off, expected_params_state_was_on", ( ( {}, { light.ATTR_HS_COLOR: (50.353, 100), light.ATTR_BRIGHTNESS: 100, light.ATTR_TRANSITION: 3, }, { light.ATTR_HS_COLOR: (50.353, 100), light.ATTR_BRIGHTNESS: 100, light.ATTR_TRANSITION: 3, }, ), ( {light.ATTR_BRIGHTNESS: 22}, { light.ATTR_HS_COLOR: (50.353, 100), light.ATTR_BRIGHTNESS: 22, light.ATTR_TRANSITION: 3, }, { light.ATTR_BRIGHTNESS: 22, light.ATTR_TRANSITION: 3, }, ), ( {light.ATTR_TRANSITION: 22}, { light.ATTR_HS_COLOR: (50.353, 100), light.ATTR_BRIGHTNESS: 100, light.ATTR_TRANSITION: 22, }, { light.ATTR_TRANSITION: 22, }, ), ( { light.ATTR_XY_COLOR: [0.4448, 0.4066], light.ATTR_BRIGHTNESS: 11, light.ATTR_TRANSITION: 1, }, { light.ATTR_HS_COLOR: (38.88, 49.02), light.ATTR_BRIGHTNESS: 11, light.ATTR_TRANSITION: 1, }, { light.ATTR_HS_COLOR: (38.88, 49.02), light.ATTR_BRIGHTNESS: 11, light.ATTR_TRANSITION: 1, }, ), ( {light.ATTR_BRIGHTNESS: 11, light.ATTR_TRANSITION: 1}, { light.ATTR_HS_COLOR: (50.353, 100), light.ATTR_BRIGHTNESS: 11, light.ATTR_TRANSITION: 1, }, { light.ATTR_BRIGHTNESS: 11, light.ATTR_TRANSITION: 1, }, ), ), ) async def test_default_profiles_light( hass, mock_light_profiles, extra_call_params, enable_custom_integrations, expected_params_state_was_off, expected_params_state_was_on, ): """Test default turn-on light profile for a specific light.""" platform = getattr(hass.components, "test.light") platform.init() assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} ) await hass.async_block_till_done() profile = light.Profile("group.all_lights.default", 0.3, 0.5, 200, 0) mock_light_profiles[profile.name] = profile profile = light.Profile("light.ceiling_2.default", 0.6, 0.6, 100, 3) mock_light_profiles[profile.name] = profile dev = next(filter(lambda x: x.entity_id == "light.ceiling_2", platform.ENTITIES)) dev.supported_color_modes = [light.COLOR_MODE_HS] dev.supported_features = light.SUPPORT_TRANSITION await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: dev.entity_id, **extra_call_params, }, blocking=True, ) _, data = dev.last_call("turn_on") assert data == expected_params_state_was_off await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: dev.entity_id, **extra_call_params, }, blocking=True, ) _, data = dev.last_call("turn_on") assert data == expected_params_state_was_on await hass.services.async_call( light.DOMAIN, SERVICE_TURN_OFF, { ATTR_ENTITY_ID: dev.entity_id, }, blocking=True, ) _, data = dev.last_call("turn_off") assert data == { light.ATTR_TRANSITION: 3, } async def test_light_context(hass, hass_admin_user, enable_custom_integrations): """Test that light context works.""" platform = getattr(hass.components, "test.light") platform.init() assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get("light.ceiling") assert state is not None await hass.services.async_call( "light", "toggle", {"entity_id": state.entity_id}, blocking=True, context=core.Context(user_id=hass_admin_user.id), ) state2 = hass.states.get("light.ceiling") assert state2 is not None assert state.state != state2.state assert state2.context.user_id == hass_admin_user.id async def test_light_turn_on_auth(hass, hass_admin_user, enable_custom_integrations): """Test that light context works.""" platform = getattr(hass.components, "test.light") platform.init() assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get("light.ceiling") assert state is not None hass_admin_user.mock_policy({}) with pytest.raises(Unauthorized): await hass.services.async_call( "light", "turn_on", {"entity_id": state.entity_id}, blocking=True, context=core.Context(user_id=hass_admin_user.id), ) async def test_light_brightness_step(hass, enable_custom_integrations): """Test that light context works.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_0", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_1", STATE_ON)) entity0 = platform.ENTITIES[0] entity0.supported_features = light.SUPPORT_BRIGHTNESS entity0.brightness = 100 entity1 = platform.ENTITIES[1] entity1.supported_features = light.SUPPORT_BRIGHTNESS entity1.brightness = 50 assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state is not None assert state.attributes["brightness"] == 100 state = hass.states.get(entity1.entity_id) assert state is not None assert state.attributes["brightness"] == 50 await hass.services.async_call( "light", "turn_on", {"entity_id": [entity0.entity_id, entity1.entity_id], "brightness_step": -10}, blocking=True, ) _, data = entity0.last_call("turn_on") assert data["brightness"] == 90 # 100 - 10 _, data = entity1.last_call("turn_on") assert data["brightness"] == 40 # 50 - 10 await hass.services.async_call( "light", "turn_on", { "entity_id": [entity0.entity_id, entity1.entity_id], "brightness_step_pct": 10, }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data["brightness"] == 116 # 90 + (255 * 0.10) _, data = entity1.last_call("turn_on") assert data["brightness"] == 66 # 40 + (255 * 0.10) await hass.services.async_call( "light", "turn_on", { "entity_id": entity0.entity_id, "brightness_step": -126, }, blocking=True, ) assert entity0.state == "off" # 126 - 126; brightness is 0, light should turn off async def test_light_brightness_pct_conversion(hass, enable_custom_integrations): """Test that light brightness percent conversion.""" platform = getattr(hass.components, "test.light") platform.init() entity = platform.ENTITIES[0] entity.supported_features = light.SUPPORT_BRIGHTNESS entity.brightness = 100 assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity.entity_id) assert state is not None assert state.attributes["brightness"] == 100 await hass.services.async_call( "light", "turn_on", {"entity_id": entity.entity_id, "brightness_pct": 1}, blocking=True, ) _, data = entity.last_call("turn_on") assert data["brightness"] == 3 await hass.services.async_call( "light", "turn_on", {"entity_id": entity.entity_id, "brightness_pct": 2}, blocking=True, ) _, data = entity.last_call("turn_on") assert data["brightness"] == 5 await hass.services.async_call( "light", "turn_on", {"entity_id": entity.entity_id, "brightness_pct": 50}, blocking=True, ) _, data = entity.last_call("turn_on") assert data["brightness"] == 128 await hass.services.async_call( "light", "turn_on", {"entity_id": entity.entity_id, "brightness_pct": 99}, blocking=True, ) _, data = entity.last_call("turn_on") assert data["brightness"] == 252 await hass.services.async_call( "light", "turn_on", {"entity_id": entity.entity_id, "brightness_pct": 100}, blocking=True, ) _, data = entity.last_call("turn_on") assert data["brightness"] == 255 def test_deprecated_base_class(caplog): """Test deprecated base class.""" class CustomLight(light.Light): pass CustomLight() assert "Light is deprecated, modify CustomLight" in caplog.text async def test_profiles(hass): """Test profiles loading.""" profiles = orig_Profiles(hass) await profiles.async_initialize() assert profiles.data == { "concentrate": light.Profile("concentrate", 0.5119, 0.4147, 219, None), "energize": light.Profile("energize", 0.368, 0.3686, 203, None), "reading": light.Profile("reading", 0.4448, 0.4066, 240, None), "relax": light.Profile("relax", 0.5119, 0.4147, 144, None), } assert profiles.data["concentrate"].hs_color == (35.932, 69.412) assert profiles.data["energize"].hs_color == (43.333, 21.176) assert profiles.data["reading"].hs_color == (38.88, 49.02) assert profiles.data["relax"].hs_color == (35.932, 69.412) @patch("os.path.isfile", MagicMock(side_effect=(True, False))) async def test_profile_load_optional_hs_color(hass): """Test profile loading with profiles containing no xy color.""" csv_file = """the first line is skipped no_color,,,100,1 no_color_no_transition,,,110 color,0.5119,0.4147,120,2 color_no_transition,0.4448,0.4066,130 color_and_brightness,0.4448,0.4066,170, only_brightness,,,140 only_transition,,,,150 transition_float,,,,1.6 invalid_profile_1, invalid_color_2,,0.1,1,2 invalid_color_3,,0.1,1 invalid_color_4,0.1,,1,3 invalid_color_5,0.1,,1 invalid_brightness,0,0,256,4 invalid_brightness_2,0,0,256 invalid_no_brightness_no_color_no_transition,,, """ profiles = orig_Profiles(hass) with patch("builtins.open", mock_open(read_data=csv_file)): await profiles.async_initialize() await hass.async_block_till_done() assert profiles.data["no_color"].hs_color is None assert profiles.data["no_color"].brightness == 100 assert profiles.data["no_color"].transition == 1 assert profiles.data["no_color_no_transition"].hs_color is None assert profiles.data["no_color_no_transition"].brightness == 110 assert profiles.data["no_color_no_transition"].transition is None assert profiles.data["color"].hs_color == (35.932, 69.412) assert profiles.data["color"].brightness == 120 assert profiles.data["color"].transition == 2 assert profiles.data["color_no_transition"].hs_color == (38.88, 49.02) assert profiles.data["color_no_transition"].brightness == 130 assert profiles.data["color_no_transition"].transition is None assert profiles.data["color_and_brightness"].hs_color == (38.88, 49.02) assert profiles.data["color_and_brightness"].brightness == 170 assert profiles.data["color_and_brightness"].transition is None assert profiles.data["only_brightness"].hs_color is None assert profiles.data["only_brightness"].brightness == 140 assert profiles.data["only_brightness"].transition is None assert profiles.data["only_transition"].hs_color is None assert profiles.data["only_transition"].brightness is None assert profiles.data["only_transition"].transition == 150 assert profiles.data["transition_float"].hs_color is None assert profiles.data["transition_float"].brightness is None assert profiles.data["transition_float"].transition == 1.6 for invalid_profile_name in ( "invalid_profile_1", "invalid_color_2", "invalid_color_3", "invalid_color_4", "invalid_color_5", "invalid_brightness", "invalid_brightness_2", "invalid_no_brightness_no_color_no_transition", ): assert invalid_profile_name not in profiles.data @pytest.mark.parametrize("light_state", (STATE_ON, STATE_OFF)) async def test_light_backwards_compatibility_supported_color_modes( hass, light_state, enable_custom_integrations ): """Test supported_color_modes if not implemented by the entity.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_0", light_state)) platform.ENTITIES.append(platform.MockLight("Test_1", light_state)) platform.ENTITIES.append(platform.MockLight("Test_2", light_state)) platform.ENTITIES.append(platform.MockLight("Test_3", light_state)) platform.ENTITIES.append(platform.MockLight("Test_4", light_state)) platform.ENTITIES.append(platform.MockLight("Test_5", light_state)) platform.ENTITIES.append(platform.MockLight("Test_6", light_state)) entity0 = platform.ENTITIES[0] entity1 = platform.ENTITIES[1] entity1.supported_features = light.SUPPORT_BRIGHTNESS entity2 = platform.ENTITIES[2] entity2.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR_TEMP entity3 = platform.ENTITIES[3] entity3.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR entity4 = platform.ENTITIES[4] entity4.supported_features = ( light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_WHITE_VALUE ) entity5 = platform.ENTITIES[5] entity5.supported_features = ( light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_COLOR_TEMP ) entity6 = platform.ENTITIES[6] entity6.supported_features = ( light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_COLOR_TEMP | light.SUPPORT_WHITE_VALUE ) assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_ONOFF] if light_state == STATE_OFF: assert "color_mode" not in state.attributes else: assert state.attributes["color_mode"] == light.COLOR_MODE_ONOFF state = hass.states.get(entity1.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_BRIGHTNESS] if light_state == STATE_OFF: assert "color_mode" not in state.attributes else: assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN state = hass.states.get(entity2.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_COLOR_TEMP] if light_state == STATE_OFF: assert "color_mode" not in state.attributes else: assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN state = hass.states.get(entity3.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS] if light_state == STATE_OFF: assert "color_mode" not in state.attributes else: assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN state = hass.states.get(entity4.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_HS, light.COLOR_MODE_RGBW, ] if light_state == STATE_OFF: assert "color_mode" not in state.attributes else: assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN state = hass.states.get(entity5.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_COLOR_TEMP, light.COLOR_MODE_HS, ] if light_state == STATE_OFF: assert "color_mode" not in state.attributes else: assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN state = hass.states.get(entity6.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_COLOR_TEMP, light.COLOR_MODE_HS, light.COLOR_MODE_RGBW, ] if light_state == STATE_OFF: assert "color_mode" not in state.attributes else: assert state.attributes["color_mode"] == light.COLOR_MODE_UNKNOWN async def test_light_backwards_compatibility_color_mode( hass, enable_custom_integrations ): """Test color_mode if not implemented by the entity.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_0", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_1", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_2", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_3", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_4", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_5", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_6", STATE_ON)) entity0 = platform.ENTITIES[0] entity1 = platform.ENTITIES[1] entity1.supported_features = light.SUPPORT_BRIGHTNESS entity1.brightness = 100 entity2 = platform.ENTITIES[2] entity2.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR_TEMP entity2.color_temp = 100 entity3 = platform.ENTITIES[3] entity3.supported_features = light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR entity3.hs_color = (240, 100) entity4 = platform.ENTITIES[4] entity4.supported_features = ( light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_WHITE_VALUE ) entity4.hs_color = (240, 100) entity4.white_value = 100 entity5 = platform.ENTITIES[5] entity5.supported_features = ( light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_COLOR_TEMP ) entity5.hs_color = (240, 100) entity5.color_temp = 100 assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_ONOFF] assert state.attributes["color_mode"] == light.COLOR_MODE_ONOFF state = hass.states.get(entity1.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_BRIGHTNESS] assert state.attributes["color_mode"] == light.COLOR_MODE_BRIGHTNESS state = hass.states.get(entity2.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_COLOR_TEMP] assert state.attributes["color_mode"] == light.COLOR_MODE_COLOR_TEMP assert state.attributes["rgb_color"] == (201, 218, 255) assert state.attributes["hs_color"] == (221.575, 20.9) assert state.attributes["xy_color"] == (0.277, 0.287) state = hass.states.get(entity3.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS] assert state.attributes["color_mode"] == light.COLOR_MODE_HS state = hass.states.get(entity4.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_HS, light.COLOR_MODE_RGBW, ] assert state.attributes["color_mode"] == light.COLOR_MODE_RGBW state = hass.states.get(entity5.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_COLOR_TEMP, light.COLOR_MODE_HS, ] # hs color prioritized over color_temp, light should report mode COLOR_MODE_HS assert state.attributes["color_mode"] == light.COLOR_MODE_HS async def test_light_service_call_rgbw(hass, enable_custom_integrations): """Test backwards compatibility for rgbw functionality in service calls.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_legacy_white_value", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgbw", STATE_ON)) entity0 = platform.ENTITIES[0] entity0.supported_features = ( light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_WHITE_VALUE ) entity1 = platform.ENTITIES[1] entity1.supported_color_modes = {light.COLOR_MODE_RGBW} assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_HS, light.COLOR_MODE_RGBW, ] state = hass.states.get(entity1.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_RGBW] await hass.services.async_call( "light", "turn_on", { "entity_id": [entity0.entity_id, entity1.entity_id], "brightness_pct": 100, "rgbw_color": (10, 20, 30, 40), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (210.0, 66.667), "white_value": 40} _, data = entity1.last_call("turn_on") assert data == {"brightness": 255, "rgbw_color": (10, 20, 30, 40)} async def test_light_state_rgbw(hass, enable_custom_integrations): """Test rgbw color conversion in state updates.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_legacy_white_value", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgbw", STATE_ON)) entity0 = platform.ENTITIES[0] legacy_supported_features = ( light.SUPPORT_BRIGHTNESS | light.SUPPORT_COLOR | light.SUPPORT_WHITE_VALUE ) entity0.supported_features = legacy_supported_features entity0.hs_color = (210.0, 66.667) entity0.rgb_color = "Invalid" # Should be ignored entity0.rgbww_color = "Invalid" # Should be ignored entity0.white_value = 40 entity0.xy_color = "Invalid" # Should be ignored entity1 = platform.ENTITIES[1] entity1.supported_color_modes = {light.COLOR_MODE_RGBW} entity1.color_mode = light.COLOR_MODE_RGBW entity1.hs_color = "Invalid" # Should be ignored entity1.rgb_color = "Invalid" # Should be ignored entity1.rgbw_color = (1, 2, 3, 4) entity1.rgbww_color = "Invalid" # Should be ignored entity1.white_value = "Invalid" # Should be ignored entity1.xy_color = "Invalid" # Should be ignored assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state.attributes == { "color_mode": light.COLOR_MODE_RGBW, "friendly_name": "Test_legacy_white_value", "supported_color_modes": [light.COLOR_MODE_HS, light.COLOR_MODE_RGBW], "supported_features": legacy_supported_features, "hs_color": (210.0, 66.667), "rgb_color": (84, 169, 255), "rgbw_color": (84, 169, 255, 40), "white_value": 40, "xy_color": (0.173, 0.207), } state = hass.states.get(entity1.entity_id) assert state.attributes == { "color_mode": light.COLOR_MODE_RGBW, "friendly_name": "Test_rgbw", "supported_color_modes": [light.COLOR_MODE_RGBW], "supported_features": 0, "hs_color": (240.0, 25.0), "rgb_color": (3, 3, 4), "rgbw_color": (1, 2, 3, 4), "xy_color": (0.301, 0.295), } async def test_light_state_rgbww(hass, enable_custom_integrations): """Test rgbww color conversion in state updates.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_rgbww", STATE_ON)) entity0 = platform.ENTITIES[0] entity0.supported_color_modes = {light.COLOR_MODE_RGBWW} entity0.color_mode = light.COLOR_MODE_RGBWW entity0.hs_color = "Invalid" # Should be ignored entity0.rgb_color = "Invalid" # Should be ignored entity0.rgbw_color = "Invalid" # Should be ignored entity0.rgbww_color = (1, 2, 3, 4, 5) entity0.white_value = "Invalid" # Should be ignored entity0.xy_color = "Invalid" # Should be ignored assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert dict(state.attributes) == { "color_mode": light.COLOR_MODE_RGBWW, "friendly_name": "Test_rgbww", "supported_color_modes": [light.COLOR_MODE_RGBWW], "supported_features": 0, "hs_color": (60.0, 20.0), "rgb_color": (5, 5, 4), "rgbww_color": (1, 2, 3, 4, 5), "xy_color": (0.339, 0.354), } async def test_light_service_call_color_conversion(hass, enable_custom_integrations): """Test color conversion in service calls.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_hs", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgb", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_xy", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_all", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_legacy", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgbw", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgbww", STATE_ON)) entity0 = platform.ENTITIES[0] entity0.supported_color_modes = {light.COLOR_MODE_HS} entity1 = platform.ENTITIES[1] entity1.supported_color_modes = {light.COLOR_MODE_RGB} entity2 = platform.ENTITIES[2] entity2.supported_color_modes = {light.COLOR_MODE_XY} entity3 = platform.ENTITIES[3] entity3.supported_color_modes = { light.COLOR_MODE_HS, light.COLOR_MODE_RGB, light.COLOR_MODE_XY, } entity4 = platform.ENTITIES[4] entity4.supported_features = light.SUPPORT_COLOR entity5 = platform.ENTITIES[5] entity5.supported_color_modes = {light.COLOR_MODE_RGBW} entity6 = platform.ENTITIES[6] entity6.supported_color_modes = {light.COLOR_MODE_RGBWW} assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS] state = hass.states.get(entity1.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_RGB] state = hass.states.get(entity2.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_XY] state = hass.states.get(entity3.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_HS, light.COLOR_MODE_RGB, light.COLOR_MODE_XY, ] state = hass.states.get(entity4.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS] state = hass.states.get(entity5.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_RGBW] state = hass.states.get(entity6.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_RGBWW] await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 100, "hs_color": (240, 100), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (240.0, 100.0)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 255, "rgb_color": (0, 0, 255)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 255, "xy_color": (0.136, 0.04)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (240.0, 100.0)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (240.0, 100.0)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 255, "rgbw_color": (0, 0, 255, 0)} _, data = entity6.last_call("turn_on") assert data == {"brightness": 255, "rgbww_color": (0, 0, 255, 0, 0)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 100, "hs_color": (240, 0), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (240.0, 0.0)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 255, "rgb_color": (255, 255, 255)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 255, "xy_color": (0.323, 0.329)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (240.0, 0.0)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (240.0, 0.0)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 255, "rgbw_color": (0, 0, 0, 255)} _, data = entity6.last_call("turn_on") # The midpoint the the white channels is warm, compensated by adding green + blue assert data == {"brightness": 255, "rgbww_color": (0, 76, 141, 255, 255)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 50, "rgb_color": (128, 0, 0), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 100.0)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (128, 0, 0)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.701, 0.299)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (128, 0, 0)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 100.0)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 128, "rgbw_color": (128, 0, 0, 0)} _, data = entity6.last_call("turn_on") assert data == {"brightness": 128, "rgbww_color": (128, 0, 0, 0, 0)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 50, "rgb_color": (255, 255, 255), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 0.0)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (255, 255, 255)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.323, 0.329)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (255, 255, 255)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 0.0)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 128, "rgbw_color": (0, 0, 0, 255)} _, data = entity6.last_call("turn_on") # The midpoint the the white channels is warm, compensated by adding green + blue assert data == {"brightness": 128, "rgbww_color": (0, 76, 141, 255, 255)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 50, "xy_color": (0.1, 0.8), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (125.176, 100.0)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (0, 255, 22)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.1, 0.8)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.1, 0.8)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (125.176, 100.0)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 128, "rgbw_color": (0, 255, 22, 0)} _, data = entity6.last_call("turn_on") assert data == {"brightness": 128, "rgbww_color": (0, 255, 22, 0, 0)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 50, "xy_color": (0.323, 0.329), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 0.392)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (255, 254, 254)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.323, 0.329)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.323, 0.329)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 0.392)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 128, "rgbw_color": (1, 0, 0, 255)} _, data = entity6.last_call("turn_on") # The midpoint the the white channels is warm, compensated by adding green + blue assert data == {"brightness": 128, "rgbww_color": (0, 75, 140, 255, 255)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 50, "rgbw_color": (128, 0, 0, 64), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 66.406)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (128, 43, 43)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.592, 0.308)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (128, 43, 43)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 66.406)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 128, "rgbw_color": (128, 0, 0, 64)} _, data = entity6.last_call("turn_on") # The midpoint the the white channels is warm, compensated by adding green + blue assert data == {"brightness": 128, "rgbww_color": (128, 0, 30, 117, 117)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 50, "rgbw_color": (255, 255, 255, 255), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 0.0)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (255, 255, 255)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.323, 0.329)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (255, 255, 255)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (0.0, 0.0)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 128, "rgbw_color": (255, 255, 255, 255)} _, data = entity6.last_call("turn_on") # The midpoint the the white channels is warm, compensated by adding green + blue assert data == {"brightness": 128, "rgbww_color": (0, 76, 141, 255, 255)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 50, "rgbww_color": (128, 0, 0, 64, 32), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (4.118, 79.688)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (128, 33, 26)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.639, 0.312)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (128, 33, 26)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (4.118, 79.688)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 128, "rgbw_color": (128, 9, 0, 33)} _, data = entity6.last_call("turn_on") assert data == {"brightness": 128, "rgbww_color": (128, 0, 0, 64, 32)} await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 50, "rgbww_color": (255, 255, 255, 255, 255), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (27.429, 27.451)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (255, 217, 185)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 128, "xy_color": (0.396, 0.359)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 128, "rgb_color": (255, 217, 185)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 128, "hs_color": (27.429, 27.451)} _, data = entity5.last_call("turn_on") # The midpoint the the white channels is warm, compensated by decreasing green + blue assert data == {"brightness": 128, "rgbw_color": (96, 44, 0, 255)} _, data = entity6.last_call("turn_on") assert data == {"brightness": 128, "rgbww_color": (255, 255, 255, 255, 255)} async def test_light_service_call_color_conversion_named_tuple( hass, enable_custom_integrations ): """Test a named tuple (RGBColor) is handled correctly.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_hs", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgb", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_xy", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_all", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_legacy", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgbw", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgbww", STATE_ON)) entity0 = platform.ENTITIES[0] entity0.supported_color_modes = {light.COLOR_MODE_HS} entity1 = platform.ENTITIES[1] entity1.supported_color_modes = {light.COLOR_MODE_RGB} entity2 = platform.ENTITIES[2] entity2.supported_color_modes = {light.COLOR_MODE_XY} entity3 = platform.ENTITIES[3] entity3.supported_color_modes = { light.COLOR_MODE_HS, light.COLOR_MODE_RGB, light.COLOR_MODE_XY, } entity4 = platform.ENTITIES[4] entity4.supported_features = light.SUPPORT_COLOR entity5 = platform.ENTITIES[5] entity5.supported_color_modes = {light.COLOR_MODE_RGBW} entity6 = platform.ENTITIES[6] entity6.supported_color_modes = {light.COLOR_MODE_RGBWW} assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, entity3.entity_id, entity4.entity_id, entity5.entity_id, entity6.entity_id, ], "brightness_pct": 25, "rgb_color": color_util.RGBColor(128, 0, 0), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 64, "hs_color": (0.0, 100.0)} _, data = entity1.last_call("turn_on") assert data == {"brightness": 64, "rgb_color": (128, 0, 0)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 64, "xy_color": (0.701, 0.299)} _, data = entity3.last_call("turn_on") assert data == {"brightness": 64, "rgb_color": (128, 0, 0)} _, data = entity4.last_call("turn_on") assert data == {"brightness": 64, "hs_color": (0.0, 100.0)} _, data = entity5.last_call("turn_on") assert data == {"brightness": 64, "rgbw_color": (128, 0, 0, 0)} _, data = entity6.last_call("turn_on") assert data == {"brightness": 64, "rgbww_color": (128, 0, 0, 0, 0)} async def test_light_service_call_color_temp_emulation( hass, enable_custom_integrations ): """Test color conversion in service calls.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_hs_ct", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_hs", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_hs_white", STATE_ON)) entity0 = platform.ENTITIES[0] entity0.supported_color_modes = {light.COLOR_MODE_COLOR_TEMP, light.COLOR_MODE_HS} entity1 = platform.ENTITIES[1] entity1.supported_color_modes = {light.COLOR_MODE_HS} entity2 = platform.ENTITIES[2] entity2.supported_color_modes = {light.COLOR_MODE_HS, light.COLOR_MODE_WHITE} assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_COLOR_TEMP, light.COLOR_MODE_HS, ] state = hass.states.get(entity1.entity_id) assert state.attributes["supported_color_modes"] == [light.COLOR_MODE_HS] state = hass.states.get(entity2.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_HS, light.COLOR_MODE_WHITE, ] await hass.services.async_call( "light", "turn_on", { "entity_id": [ entity0.entity_id, entity1.entity_id, entity2.entity_id, ], "brightness_pct": 100, "color_temp": 200, }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 255, "color_temp": 200} _, data = entity1.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (27.001, 19.243)} _, data = entity2.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (27.001, 19.243)} async def test_light_service_call_white_mode(hass, enable_custom_integrations): """Test color_mode white in service calls.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_white", STATE_ON)) entity0 = platform.ENTITIES[0] entity0.supported_color_modes = {light.COLOR_MODE_HS, light.COLOR_MODE_WHITE} assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state.attributes["supported_color_modes"] == [ light.COLOR_MODE_HS, light.COLOR_MODE_WHITE, ] await hass.services.async_call( "light", "turn_on", { "entity_id": [entity0.entity_id], "brightness_pct": 100, "hs_color": (240, 100), }, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"brightness": 255, "hs_color": (240.0, 100.0)} entity0.calls = [] await hass.services.async_call( "light", "turn_on", {"entity_id": [entity0.entity_id], "white": 50}, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"white": 50} entity0.calls = [] await hass.services.async_call( "light", "turn_on", {"entity_id": [entity0.entity_id], "white": 0}, blocking=True, ) _, data = entity0.last_call("turn_off") assert data == {} entity0.calls = [] await hass.services.async_call( "light", "turn_on", {"entity_id": [entity0.entity_id], "brightness_pct": 100, "white": 50}, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"white": 255} entity0.calls = [] await hass.services.async_call( "light", "turn_on", {"entity_id": [entity0.entity_id], "brightness": 100, "white": 0}, blocking=True, ) _, data = entity0.last_call("turn_on") assert data == {"white": 100} entity0.calls = [] await hass.services.async_call( "light", "turn_on", {"entity_id": [entity0.entity_id], "brightness_pct": 0, "white": 50}, blocking=True, ) _, data = entity0.last_call("turn_off") assert data == {} async def test_light_state_color_conversion(hass, enable_custom_integrations): """Test color conversion in state updates.""" platform = getattr(hass.components, "test.light") platform.init(empty=True) platform.ENTITIES.append(platform.MockLight("Test_hs", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_rgb", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_xy", STATE_ON)) platform.ENTITIES.append(platform.MockLight("Test_legacy", STATE_ON)) entity0 = platform.ENTITIES[0] entity0.supported_color_modes = {light.COLOR_MODE_HS} entity0.color_mode = light.COLOR_MODE_HS entity0.hs_color = (240, 100) entity0.rgb_color = "Invalid" # Should be ignored entity0.xy_color = "Invalid" # Should be ignored entity1 = platform.ENTITIES[1] entity1.supported_color_modes = {light.COLOR_MODE_RGB} entity1.color_mode = light.COLOR_MODE_RGB entity1.hs_color = "Invalid" # Should be ignored entity1.rgb_color = (128, 0, 0) entity1.xy_color = "Invalid" # Should be ignored entity2 = platform.ENTITIES[2] entity2.supported_color_modes = {light.COLOR_MODE_XY} entity2.color_mode = light.COLOR_MODE_XY entity2.hs_color = "Invalid" # Should be ignored entity2.rgb_color = "Invalid" # Should be ignored entity2.xy_color = (0.1, 0.8) entity3 = platform.ENTITIES[3] entity3.hs_color = (240, 100) entity3.supported_features = light.SUPPORT_COLOR assert await async_setup_component(hass, "light", {"light": {"platform": "test"}}) await hass.async_block_till_done() state = hass.states.get(entity0.entity_id) assert state.attributes["color_mode"] == light.COLOR_MODE_HS assert state.attributes["hs_color"] == (240, 100) assert state.attributes["rgb_color"] == (0, 0, 255) assert state.attributes["xy_color"] == (0.136, 0.04) state = hass.states.get(entity1.entity_id) assert state.attributes["color_mode"] == light.COLOR_MODE_RGB assert state.attributes["hs_color"] == (0.0, 100.0) assert state.attributes["rgb_color"] == (128, 0, 0) assert state.attributes["xy_color"] == (0.701, 0.299) state = hass.states.get(entity2.entity_id) assert state.attributes["color_mode"] == light.COLOR_MODE_XY assert state.attributes["hs_color"] == (125.176, 100.0) assert state.attributes["rgb_color"] == (0, 255, 22) assert state.attributes["xy_color"] == (0.1, 0.8) state = hass.states.get(entity3.entity_id) assert state.attributes["color_mode"] == light.COLOR_MODE_HS assert state.attributes["hs_color"] == (240, 100) assert state.attributes["rgb_color"] == (0, 0, 255) assert state.attributes["xy_color"] == (0.136, 0.04) async def test_services_filter_parameters( hass, mock_light_profiles, enable_custom_integrations ): """Test turn_on and turn_off filters unsupported parameters.""" platform = getattr(hass.components, "test.light") platform.init() assert await async_setup_component( hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}} ) await hass.async_block_till_done() ent1, _, _ = platform.ENTITIES # turn off the light by setting brightness to 0, this should work even if the light # doesn't support brightness await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True ) await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL, light.ATTR_BRIGHTNESS: 0}, blocking=True, ) assert not light.is_on(hass, ent1.entity_id) # Ensure all unsupported attributes are filtered when light is turned on await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_BRIGHTNESS: 0, light.ATTR_EFFECT: "fun_effect", light.ATTR_FLASH: "short", light.ATTR_TRANSITION: 10, light.ATTR_WHITE_VALUE: 0, }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_COLOR_TEMP: 153, }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_HS_COLOR: (0, 0), }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_RGB_COLOR: (0, 0, 0), }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_RGBW_COLOR: (0, 0, 0, 0), }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_RGBWW_COLOR: (0, 0, 0, 0, 0), }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_XY_COLOR: (0, 0), }, blocking=True, ) _, data = ent1.last_call("turn_on") assert data == {} # Ensure all unsupported attributes are filtered when light is turned off await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_BRIGHTNESS: 0, light.ATTR_EFFECT: "fun_effect", light.ATTR_FLASH: "short", light.ATTR_TRANSITION: 10, light.ATTR_WHITE_VALUE: 0, }, blocking=True, ) assert not light.is_on(hass, ent1.entity_id) _, data = ent1.last_call("turn_off") assert data == {} await hass.services.async_call( light.DOMAIN, SERVICE_TURN_OFF, { ATTR_ENTITY_ID: ent1.entity_id, light.ATTR_FLASH: "short", light.ATTR_TRANSITION: 10, }, blocking=True, ) assert not light.is_on(hass, ent1.entity_id) _, data = ent1.last_call("turn_off") assert data == {} def test_valid_supported_color_modes(): """Test valid_supported_color_modes.""" supported = {light.COLOR_MODE_HS} assert light.valid_supported_color_modes(supported) == supported # Supported color modes must not be empty supported = set() with pytest.raises(vol.Error): light.valid_supported_color_modes(supported) # COLOR_MODE_WHITE must be combined with a color mode supporting color supported = {light.COLOR_MODE_WHITE} with pytest.raises(vol.Error): light.valid_supported_color_modes(supported) supported = {light.COLOR_MODE_WHITE, light.COLOR_MODE_COLOR_TEMP} with pytest.raises(vol.Error): light.valid_supported_color_modes(supported) supported = {light.COLOR_MODE_WHITE, light.COLOR_MODE_HS} assert light.valid_supported_color_modes(supported) == supported # COLOR_MODE_ONOFF must be the only supported mode supported = {light.COLOR_MODE_ONOFF} assert light.valid_supported_color_modes(supported) == supported supported = {light.COLOR_MODE_ONOFF, light.COLOR_MODE_COLOR_TEMP} with pytest.raises(vol.Error): light.valid_supported_color_modes(supported) # COLOR_MODE_BRIGHTNESS must be the only supported mode supported = {light.COLOR_MODE_BRIGHTNESS} assert light.valid_supported_color_modes(supported) == supported supported = {light.COLOR_MODE_BRIGHTNESS, light.COLOR_MODE_COLOR_TEMP} with pytest.raises(vol.Error): light.valid_supported_color_modes(supported)