diff --git a/homeassistant/components/emulated_hue/hue_api.py b/homeassistant/components/emulated_hue/hue_api.py index 51580a28adf..1630405a73e 100644 --- a/homeassistant/components/emulated_hue/hue_api.py +++ b/homeassistant/components/emulated_hue/hue_api.py @@ -43,9 +43,12 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, + ATTR_TRANSITION, + ATTR_XY_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, + SUPPORT_TRANSITION, ) from homeassistant.components.media_player.const import ( ATTR_MEDIA_VOLUME_LEVEL, @@ -82,6 +85,8 @@ STATE_COLORMODE = "colormode" STATE_HUE = "hue" STATE_SATURATION = "sat" STATE_COLOR_TEMP = "ct" +STATE_TRANSITON = "tt" +STATE_XY = "xy" # Hue API states, defined separately in case they change HUE_API_STATE_ON = "on" @@ -90,7 +95,9 @@ HUE_API_STATE_COLORMODE = "colormode" HUE_API_STATE_HUE = "hue" HUE_API_STATE_SAT = "sat" HUE_API_STATE_CT = "ct" +HUE_API_STATE_XY = "xy" HUE_API_STATE_EFFECT = "effect" +HUE_API_STATE_TRANSITION = "transitiontime" # Hue API min/max values - https://developers.meethue.com/develop/hue-api/lights-api/ HUE_API_STATE_BRI_MIN = 1 # Brightness @@ -357,6 +364,8 @@ class HueOneLightChangeView(HomeAssistantView): STATE_HUE: None, STATE_SATURATION: None, STATE_COLOR_TEMP: None, + STATE_XY: None, + STATE_TRANSITON: None, } if HUE_API_STATE_ON in request_json: @@ -372,6 +381,7 @@ class HueOneLightChangeView(HomeAssistantView): (HUE_API_STATE_HUE, STATE_HUE), (HUE_API_STATE_SAT, STATE_SATURATION), (HUE_API_STATE_CT, STATE_COLOR_TEMP), + (HUE_API_STATE_TRANSITION, STATE_TRANSITON), ): if key in request_json: try: @@ -379,6 +389,17 @@ class HueOneLightChangeView(HomeAssistantView): except ValueError: _LOGGER.error("Unable to parse data (2): %s", request_json) return self.json_message("Bad request", HTTP_BAD_REQUEST) + if HUE_API_STATE_XY in request_json: + try: + parsed[STATE_XY] = tuple( + ( + float(request_json[HUE_API_STATE_XY][0]), + float(request_json[HUE_API_STATE_XY][1]), + ) + ) + except ValueError: + _LOGGER.error("Unable to parse data (2): %s", request_json) + return self.json_message("Bad request", HTTP_BAD_REQUEST) if HUE_API_STATE_BRI in request_json: if entity.domain == light.DOMAIN: @@ -444,10 +465,17 @@ class HueOneLightChangeView(HomeAssistantView): data[ATTR_HS_COLOR] = (hue, sat) + if parsed[STATE_XY] is not None: + data[ATTR_XY_COLOR] = parsed[STATE_XY] + if entity_features & SUPPORT_COLOR_TEMP: if parsed[STATE_COLOR_TEMP] is not None: data[ATTR_COLOR_TEMP] = parsed[STATE_COLOR_TEMP] + if entity_features & SUPPORT_TRANSITION: + if parsed[STATE_TRANSITON] is not None: + data[ATTR_TRANSITION] = parsed[STATE_TRANSITON] / 10 + # If the requested entity is a script, add some variables elif entity.domain == script.DOMAIN: data["variables"] = { @@ -557,6 +585,8 @@ class HueOneLightChangeView(HomeAssistantView): (STATE_HUE, HUE_API_STATE_HUE), (STATE_SATURATION, HUE_API_STATE_SAT), (STATE_COLOR_TEMP, HUE_API_STATE_CT), + (STATE_XY, HUE_API_STATE_XY), + (STATE_TRANSITON, HUE_API_STATE_TRANSITION), ): if parsed[key] is not None: json_response.append( diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index 04832f4adc7..e3f965616f9 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -28,6 +28,8 @@ from homeassistant.components.emulated_hue.hue_api import ( HUE_API_STATE_HUE, HUE_API_STATE_ON, HUE_API_STATE_SAT, + HUE_API_STATE_TRANSITION, + HUE_API_STATE_XY, HUE_API_USERNAME, HueAllGroupsStateView, HueAllLightsStateView, @@ -39,6 +41,7 @@ from homeassistant.components.emulated_hue.hue_api import ( ) from homeassistant.const import ( ATTR_ENTITY_ID, + ATTR_SUPPORTED_FEATURES, CONTENT_TYPE_JSON, HTTP_NOT_FOUND, HTTP_OK, @@ -51,7 +54,11 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.util.dt as dt_util -from tests.common import async_fire_time_changed, get_test_instance_port +from tests.common import ( + async_fire_time_changed, + async_mock_service, + get_test_instance_port, +) HTTP_SERVER_PORT = get_test_instance_port() BRIDGE_SERVER_PORT = get_test_instance_port() @@ -663,6 +670,25 @@ async def test_put_light_state(hass, hass_hue, hue_client): assert ceiling_json["state"][HUE_API_STATE_HUE] == 4369 assert ceiling_json["state"][HUE_API_STATE_SAT] == 127 + # update light state through api + await perform_put_light_state( + hass_hue, + hue_client, + "light.ceiling_lights", + True, + brightness=100, + xy=((0.488, 0.48)), + ) + + # go through api to get the state back + ceiling_json = await perform_get_light_state( + hue_client, "light.ceiling_lights", HTTP_OK + ) + assert ceiling_json["state"][HUE_API_STATE_BRI] == 100 + assert hass.states.get("light.ceiling_lights").attributes[light.ATTR_XY_COLOR] == ( + (0.488, 0.48) + ) + # Go through the API to turn it off ceiling_result = await perform_put_light_state( hass_hue, hue_client, "light.ceiling_lights", False @@ -714,6 +740,30 @@ async def test_put_light_state(hass, hass_hue, hue_client): == 50 ) + # mock light.turn_on call + hass.states.async_set( + "light.ceiling_lights", STATE_ON, {ATTR_SUPPORTED_FEATURES: 55} + ) + call_turn_on = async_mock_service(hass, "light", "turn_on") + + # update light state through api + await perform_put_light_state( + hass_hue, + hue_client, + "light.ceiling_lights", + True, + brightness=99, + xy=((0.488, 0.48)), + transitiontime=60, + ) + + await hass.async_block_till_done() + assert call_turn_on[0] + assert call_turn_on[0].data[ATTR_ENTITY_ID] == ["light.ceiling_lights"] + assert call_turn_on[0].data[light.ATTR_BRIGHTNESS] == 99 + assert call_turn_on[0].data[light.ATTR_XY_COLOR] == ((0.488, 0.48)) + assert call_turn_on[0].data[light.ATTR_TRANSITION] == 6 + async def test_put_light_state_script(hass, hass_hue, hue_client): """Test the setting of script variables.""" @@ -1173,6 +1223,8 @@ async def perform_put_light_state( saturation=None, color_temp=None, with_state=True, + xy=None, + transitiontime=None, ): """Test the setting of a light state.""" req_headers = {"Content-Type": content_type} @@ -1188,8 +1240,12 @@ async def perform_put_light_state( data[HUE_API_STATE_HUE] = hue if saturation is not None: data[HUE_API_STATE_SAT] = saturation + if xy is not None: + data[HUE_API_STATE_XY] = xy if color_temp is not None: data[HUE_API_STATE_CT] = color_temp + if transitiontime is not None: + data[HUE_API_STATE_TRANSITION] = transitiontime entity_number = ENTITY_NUMBERS_BY_ID[entity_id] result = await client.put(