diff --git a/homeassistant/components/hue/scene.py b/homeassistant/components/hue/scene.py index d67a3b097c7..90c1bddc970 100644 --- a/homeassistant/components/hue/scene.py +++ b/homeassistant/components/hue/scene.py @@ -8,6 +8,7 @@ from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.scenes import ScenesController from aiohue.v2.models.scene import Scene as HueScene +from homeassistant.components.light import ATTR_TRANSITION from homeassistant.components.scene import Scene as SceneEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback @@ -16,6 +17,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .bridge import HueBridge from .const import DOMAIN from .v2.entity import HueBaseEntity +from .v2.helpers import normalize_hue_transition async def async_setup_entry( @@ -94,11 +96,9 @@ class HueSceneEntity(HueBaseEntity, SceneEntity): async def async_activate(self, **kwargs: Any) -> None: """Activate Hue scene.""" - transition = kwargs.get("transition") - if transition is not None: - # hue transition duration is in milliseconds - transition = int(transition * 1000) + transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION)) dynamic = kwargs.get("dynamic", self.is_dynamic) + await self.bridge.async_request_call( self.controller.recall, self.resource.id, diff --git a/homeassistant/components/hue/v2/group.py b/homeassistant/components/hue/v2/group.py index c5f7ae5d926..add3336764d 100644 --- a/homeassistant/components/hue/v2/group.py +++ b/homeassistant/components/hue/v2/group.py @@ -29,6 +29,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from ..bridge import HueBridge from ..const import CONF_ALLOW_HUE_GROUPS, DOMAIN from .entity import HueBaseEntity +from .helpers import normalize_hue_brightness, normalize_hue_transition ALLOWED_ERRORS = [ "device (groupedLight) has communication issues, command (on) may not have effect", @@ -147,17 +148,11 @@ class GroupedHueLight(HueBaseEntity, LightEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the light on.""" - transition = kwargs.get(ATTR_TRANSITION) + transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION)) xy_color = kwargs.get(ATTR_XY_COLOR) color_temp = kwargs.get(ATTR_COLOR_TEMP) - brightness = kwargs.get(ATTR_BRIGHTNESS) + brightness = normalize_hue_brightness(kwargs.get(ATTR_BRIGHTNESS)) flash = kwargs.get(ATTR_FLASH) - if brightness is not None: - # Hue uses a range of [0, 100] to control brightness. - brightness = float((brightness / 255) * 100) - if transition is not None: - # hue transition duration is in milliseconds - transition = int(transition * 1000) # NOTE: a grouped_light can only handle turn on/off # To set other features, you'll have to control the attached lights @@ -193,10 +188,7 @@ class GroupedHueLight(HueBaseEntity, LightEntity): async def async_turn_off(self, **kwargs: Any) -> None: """Turn the light off.""" - transition = kwargs.get(ATTR_TRANSITION) - if transition is not None: - # hue transition duration is in milliseconds - transition = int(transition * 1000) + transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION)) # NOTE: a grouped_light can only handle turn on/off # To set other features, you'll have to control the attached lights diff --git a/homeassistant/components/hue/v2/helpers.py b/homeassistant/components/hue/v2/helpers.py new file mode 100644 index 00000000000..1c26652ce25 --- /dev/null +++ b/homeassistant/components/hue/v2/helpers.py @@ -0,0 +1,21 @@ +"""Helper functions for Philips Hue v2.""" + + +def normalize_hue_brightness(brightness): + """Returns calculated brightness values""" + + if brightness is not None: + # Hue uses a range of [0, 100] to control brightness. + brightness = float((brightness / 255) * 100) + + return brightness + + +def normalize_hue_transition(transition): + """Returns rounded transition values""" + + if transition is not None: + # hue transition duration is in milliseconds and round them to 100ms + transition = int(round(transition, 1) * 1000) + + return transition diff --git a/homeassistant/components/hue/v2/light.py b/homeassistant/components/hue/v2/light.py index afb4c3d88bd..d6578c8ef9a 100644 --- a/homeassistant/components/hue/v2/light.py +++ b/homeassistant/components/hue/v2/light.py @@ -30,6 +30,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from ..bridge import HueBridge from ..const import DOMAIN from .entity import HueBaseEntity +from .helpers import normalize_hue_brightness, normalize_hue_transition ALLOWED_ERRORS = [ "device (light) has communication issues, command (on) may not have effect", @@ -155,17 +156,11 @@ class HueLight(HueBaseEntity, LightEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" - transition = kwargs.get(ATTR_TRANSITION) + transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION)) xy_color = kwargs.get(ATTR_XY_COLOR) color_temp = kwargs.get(ATTR_COLOR_TEMP) - brightness = kwargs.get(ATTR_BRIGHTNESS) + brightness = normalize_hue_brightness(kwargs.get(ATTR_BRIGHTNESS)) flash = kwargs.get(ATTR_FLASH) - if brightness is not None: - # Hue uses a range of [0, 100] to control brightness. - brightness = float((brightness / 255) * 100) - if transition is not None: - # hue transition duration is in milliseconds - transition = int(transition * 1000) await self.bridge.async_request_call( self.controller.set_state, @@ -181,11 +176,9 @@ class HueLight(HueBaseEntity, LightEntity): async def async_turn_off(self, **kwargs: Any) -> None: """Turn the light off.""" - transition = kwargs.get(ATTR_TRANSITION) + transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION)) flash = kwargs.get(ATTR_FLASH) - if transition is not None: - # hue transition duration is in milliseconds - transition = int(transition * 1000) + await self.bridge.async_request_call( self.controller.set_state, id=self.resource.id, diff --git a/tests/components/hue/test_light_v2.py b/tests/components/hue/test_light_v2.py index 95e3324e81a..936da661dd7 100644 --- a/tests/components/hue/test_light_v2.py +++ b/tests/components/hue/test_light_v2.py @@ -110,16 +110,16 @@ async def test_light_turn_on_service(hass, mock_bridge_v2, v2_resources_test_dat assert test_light.attributes["color_mode"] == COLOR_MODE_COLOR_TEMP assert test_light.attributes["brightness"] == 255 - # test again with sending transition + # test again with sending transition with 250ms which should round up to 200ms await hass.services.async_call( "light", "turn_on", - {"entity_id": test_light_id, "brightness_pct": 50, "transition": 6}, + {"entity_id": test_light_id, "brightness_pct": 50, "transition": 0.25}, blocking=True, ) assert len(mock_bridge_v2.mock_requests) == 2 assert mock_bridge_v2.mock_requests[1]["json"]["on"]["on"] is True - assert mock_bridge_v2.mock_requests[1]["json"]["dynamics"]["duration"] == 6000 + assert mock_bridge_v2.mock_requests[1]["json"]["dynamics"]["duration"] == 200 # test again with sending flash/alert await hass.services.async_call( @@ -170,12 +170,12 @@ async def test_light_turn_off_service(hass, mock_bridge_v2, v2_resources_test_da await hass.services.async_call( "light", "turn_off", - {"entity_id": test_light_id, "transition": 6}, + {"entity_id": test_light_id, "transition": 0.25}, blocking=True, ) assert len(mock_bridge_v2.mock_requests) == 2 assert mock_bridge_v2.mock_requests[1]["json"]["on"]["on"] is False - assert mock_bridge_v2.mock_requests[1]["json"]["dynamics"]["duration"] == 6000 + assert mock_bridge_v2.mock_requests[1]["json"]["dynamics"]["duration"] == 200 async def test_light_added(hass, mock_bridge_v2): @@ -310,7 +310,7 @@ async def test_grouped_lights(hass, mock_bridge_v2, v2_resources_test_data): "entity_id": test_light_id, "brightness_pct": 100, "xy_color": (0.123, 0.123), - "transition": 6, + "transition": 0.25, }, blocking=True, ) @@ -325,7 +325,7 @@ async def test_grouped_lights(hass, mock_bridge_v2, v2_resources_test_data): assert mock_bridge_v2.mock_requests[index]["json"]["color"]["xy"]["x"] == 0.123 assert mock_bridge_v2.mock_requests[index]["json"]["color"]["xy"]["y"] == 0.123 assert ( - mock_bridge_v2.mock_requests[index]["json"]["dynamics"]["duration"] == 6000 + mock_bridge_v2.mock_requests[index]["json"]["dynamics"]["duration"] == 200 ) # Now generate update events by emitting the json we've sent as incoming events @@ -374,7 +374,7 @@ async def test_grouped_lights(hass, mock_bridge_v2, v2_resources_test_data): "turn_off", { "entity_id": test_light_id, - "transition": 6, + "transition": 0.25, }, blocking=True, ) @@ -384,5 +384,5 @@ async def test_grouped_lights(hass, mock_bridge_v2, v2_resources_test_data): for index in range(0, 3): assert mock_bridge_v2.mock_requests[index]["json"]["on"]["on"] is False assert ( - mock_bridge_v2.mock_requests[index]["json"]["dynamics"]["duration"] == 6000 + mock_bridge_v2.mock_requests[index]["json"]["dynamics"]["duration"] == 200 ) diff --git a/tests/components/hue/test_scene.py b/tests/components/hue/test_scene.py index 1d270706c99..8982b70fbfb 100644 --- a/tests/components/hue/test_scene.py +++ b/tests/components/hue/test_scene.py @@ -77,13 +77,13 @@ async def test_scene_turn_on_service(hass, mock_bridge_v2, v2_resources_test_dat await hass.services.async_call( "scene", "turn_on", - {"entity_id": test_entity_id, "transition": 6}, + {"entity_id": test_entity_id, "transition": 0.25}, blocking=True, ) assert len(mock_bridge_v2.mock_requests) == 2 assert mock_bridge_v2.mock_requests[1]["json"]["recall"] == { "action": "active", - "duration": 6000, + "duration": 200, }