Round Hue transition to steps of 100ms (#62619)

* Adding round() to transition before firing turn_on, turn_off #62608
This commit is contained in:
Christian Manivong 2021-12-23 15:08:24 +01:00 committed by GitHub
parent 1bbeaa722c
commit 772428e70f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 45 additions and 39 deletions

View File

@ -8,6 +8,7 @@ from aiohue.v2.controllers.events import EventType
from aiohue.v2.controllers.scenes import ScenesController from aiohue.v2.controllers.scenes import ScenesController
from aiohue.v2.models.scene import Scene as HueScene 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.components.scene import Scene as SceneEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
@ -16,6 +17,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .bridge import HueBridge from .bridge import HueBridge
from .const import DOMAIN from .const import DOMAIN
from .v2.entity import HueBaseEntity from .v2.entity import HueBaseEntity
from .v2.helpers import normalize_hue_transition
async def async_setup_entry( async def async_setup_entry(
@ -94,11 +96,9 @@ class HueSceneEntity(HueBaseEntity, SceneEntity):
async def async_activate(self, **kwargs: Any) -> None: async def async_activate(self, **kwargs: Any) -> None:
"""Activate Hue scene.""" """Activate Hue scene."""
transition = kwargs.get("transition") transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION))
if transition is not None:
# hue transition duration is in milliseconds
transition = int(transition * 1000)
dynamic = kwargs.get("dynamic", self.is_dynamic) dynamic = kwargs.get("dynamic", self.is_dynamic)
await self.bridge.async_request_call( await self.bridge.async_request_call(
self.controller.recall, self.controller.recall,
self.resource.id, self.resource.id,

View File

@ -29,6 +29,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from ..bridge import HueBridge from ..bridge import HueBridge
from ..const import CONF_ALLOW_HUE_GROUPS, DOMAIN from ..const import CONF_ALLOW_HUE_GROUPS, DOMAIN
from .entity import HueBaseEntity from .entity import HueBaseEntity
from .helpers import normalize_hue_brightness, normalize_hue_transition
ALLOWED_ERRORS = [ ALLOWED_ERRORS = [
"device (groupedLight) has communication issues, command (on) may not have effect", "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: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on.""" """Turn the light on."""
transition = kwargs.get(ATTR_TRANSITION) transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION))
xy_color = kwargs.get(ATTR_XY_COLOR) xy_color = kwargs.get(ATTR_XY_COLOR)
color_temp = kwargs.get(ATTR_COLOR_TEMP) 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) 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 # NOTE: a grouped_light can only handle turn on/off
# To set other features, you'll have to control the attached lights # 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: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off.""" """Turn the light off."""
transition = kwargs.get(ATTR_TRANSITION) transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION))
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 # NOTE: a grouped_light can only handle turn on/off
# To set other features, you'll have to control the attached lights # To set other features, you'll have to control the attached lights

View File

@ -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

View File

@ -30,6 +30,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from ..bridge import HueBridge from ..bridge import HueBridge
from ..const import DOMAIN from ..const import DOMAIN
from .entity import HueBaseEntity from .entity import HueBaseEntity
from .helpers import normalize_hue_brightness, normalize_hue_transition
ALLOWED_ERRORS = [ ALLOWED_ERRORS = [
"device (light) has communication issues, command (on) may not have effect", "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: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on.""" """Turn the device on."""
transition = kwargs.get(ATTR_TRANSITION) transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION))
xy_color = kwargs.get(ATTR_XY_COLOR) xy_color = kwargs.get(ATTR_XY_COLOR)
color_temp = kwargs.get(ATTR_COLOR_TEMP) 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) 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( await self.bridge.async_request_call(
self.controller.set_state, self.controller.set_state,
@ -181,11 +176,9 @@ class HueLight(HueBaseEntity, LightEntity):
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off.""" """Turn the light off."""
transition = kwargs.get(ATTR_TRANSITION) transition = normalize_hue_transition(kwargs.get(ATTR_TRANSITION))
flash = kwargs.get(ATTR_FLASH) 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( await self.bridge.async_request_call(
self.controller.set_state, self.controller.set_state,
id=self.resource.id, id=self.resource.id,

View File

@ -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["color_mode"] == COLOR_MODE_COLOR_TEMP
assert test_light.attributes["brightness"] == 255 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( await hass.services.async_call(
"light", "light",
"turn_on", "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, blocking=True,
) )
assert len(mock_bridge_v2.mock_requests) == 2 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"]["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 # test again with sending flash/alert
await hass.services.async_call( 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( await hass.services.async_call(
"light", "light",
"turn_off", "turn_off",
{"entity_id": test_light_id, "transition": 6}, {"entity_id": test_light_id, "transition": 0.25},
blocking=True, blocking=True,
) )
assert len(mock_bridge_v2.mock_requests) == 2 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"]["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): 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, "entity_id": test_light_id,
"brightness_pct": 100, "brightness_pct": 100,
"xy_color": (0.123, 0.123), "xy_color": (0.123, 0.123),
"transition": 6, "transition": 0.25,
}, },
blocking=True, 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"]["x"] == 0.123
assert mock_bridge_v2.mock_requests[index]["json"]["color"]["xy"]["y"] == 0.123 assert mock_bridge_v2.mock_requests[index]["json"]["color"]["xy"]["y"] == 0.123
assert ( 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 # 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", "turn_off",
{ {
"entity_id": test_light_id, "entity_id": test_light_id,
"transition": 6, "transition": 0.25,
}, },
blocking=True, 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): 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"]["on"]["on"] is False
assert ( assert (
mock_bridge_v2.mock_requests[index]["json"]["dynamics"]["duration"] == 6000 mock_bridge_v2.mock_requests[index]["json"]["dynamics"]["duration"] == 200
) )

View File

@ -77,13 +77,13 @@ async def test_scene_turn_on_service(hass, mock_bridge_v2, v2_resources_test_dat
await hass.services.async_call( await hass.services.async_call(
"scene", "scene",
"turn_on", "turn_on",
{"entity_id": test_entity_id, "transition": 6}, {"entity_id": test_entity_id, "transition": 0.25},
blocking=True, blocking=True,
) )
assert len(mock_bridge_v2.mock_requests) == 2 assert len(mock_bridge_v2.mock_requests) == 2
assert mock_bridge_v2.mock_requests[1]["json"]["recall"] == { assert mock_bridge_v2.mock_requests[1]["json"]["recall"] == {
"action": "active", "action": "active",
"duration": 6000, "duration": 200,
} }