Fix emulated_hue brightness off by one (#34185)

* Fix emulated_hue brightness off by one

Hue uses a max brightness of 254, Home Assistant
uses a max brightness of 255. Values > 127 were
off by one.

* use constant

* fix test

* add debug

* Revert "add debug"

This reverts commit 242220a02e.
pull/33082/head
J. Nick Koston 2020-04-17 11:59:27 -05:00 committed by GitHub
parent 813f8dd63f
commit 23f278e090
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 21 deletions

View File

@ -367,7 +367,7 @@ class HueOneLightChangeView(HomeAssistantView):
cover.DOMAIN,
climate.DOMAIN,
]:
# Convert 0-255 to 0-100
# Convert 0-254 to 0-100
level = (parsed[STATE_BRIGHTNESS] / HUE_API_STATE_BRI_MAX) * 100
parsed[STATE_BRIGHTNESS] = round(level)
parsed[STATE_ON] = True
@ -390,7 +390,9 @@ class HueOneLightChangeView(HomeAssistantView):
if parsed[STATE_ON]:
if entity_features & SUPPORT_BRIGHTNESS:
if parsed[STATE_BRIGHTNESS] is not None:
data[ATTR_BRIGHTNESS] = parsed[STATE_BRIGHTNESS]
data[ATTR_BRIGHTNESS] = hue_brightness_to_hass(
parsed[STATE_BRIGHTNESS]
)
if entity_features & SUPPORT_COLOR:
if any((parsed[STATE_HUE], parsed[STATE_SATURATION])):
@ -536,7 +538,9 @@ def get_entity_state(config, entity):
data[STATE_ON] = entity.state != STATE_OFF
if data[STATE_ON]:
data[STATE_BRIGHTNESS] = entity.attributes.get(ATTR_BRIGHTNESS, 0)
data[STATE_BRIGHTNESS] = hass_to_hue_brightness(
entity.attributes.get(ATTR_BRIGHTNESS, 0)
)
hue_sat = entity.attributes.get(ATTR_HS_COLOR)
if hue_sat is not None:
hue = hue_sat[0]
@ -563,32 +567,32 @@ def get_entity_state(config, entity):
pass
elif entity.domain == climate.DOMAIN:
temperature = entity.attributes.get(ATTR_TEMPERATURE, 0)
# Convert 0-100 to 0-255
data[STATE_BRIGHTNESS] = round(temperature * 255 / 100)
# Convert 0-100 to 0-254
data[STATE_BRIGHTNESS] = round(temperature * HUE_API_STATE_BRI_MAX / 100)
elif entity.domain == media_player.DOMAIN:
level = entity.attributes.get(
ATTR_MEDIA_VOLUME_LEVEL, 1.0 if data[STATE_ON] else 0.0
)
# Convert 0.0-1.0 to 0-255
data[STATE_BRIGHTNESS] = round(min(1.0, level) * 255)
# Convert 0.0-1.0 to 0-254
data[STATE_BRIGHTNESS] = round(min(1.0, level) * HUE_API_STATE_BRI_MAX)
elif entity.domain == fan.DOMAIN:
speed = entity.attributes.get(ATTR_SPEED, 0)
# Convert 0.0-1.0 to 0-255
# Convert 0.0-1.0 to 0-254
data[STATE_BRIGHTNESS] = 0
if speed == SPEED_LOW:
data[STATE_BRIGHTNESS] = 85
elif speed == SPEED_MEDIUM:
data[STATE_BRIGHTNESS] = 170
elif speed == SPEED_HIGH:
data[STATE_BRIGHTNESS] = 255
data[STATE_BRIGHTNESS] = HUE_API_STATE_BRI_MAX
elif entity.domain == cover.DOMAIN:
level = entity.attributes.get(ATTR_CURRENT_POSITION, 0)
data[STATE_BRIGHTNESS] = round(level / 100 * 255)
data[STATE_BRIGHTNESS] = round(level / 100 * HUE_API_STATE_BRI_MAX)
else:
data = cached_state
# Make sure brightness is valid
if data[STATE_BRIGHTNESS] is None:
data[STATE_BRIGHTNESS] = 255 if data[STATE_ON] else 0
data[STATE_BRIGHTNESS] = HUE_API_STATE_BRI_MAX if data[STATE_ON] else 0
# Make sure hue/saturation are valid
if (data[STATE_HUE] is None) or (data[STATE_SATURATION] is None):
@ -723,3 +727,13 @@ def create_list_of_entities(config, request):
json_response[number] = entity_to_json(config, entity)
return json_response
def hue_brightness_to_hass(value):
"""Convert hue brightness 1..254 to hass format 0..255."""
return min(255, round((value / HUE_API_STATE_BRI_MAX) * 255))
def hass_to_hue_brightness(value):
"""Convert hass brightness 0..255 to hue 1..254 scale."""
return max(1, round((value / 255) * HUE_API_STATE_BRI_MAX))

View File

@ -384,7 +384,7 @@ async def test_get_light_state(hass_hue, hue_client):
await perform_get_light_state(hue_client, "light.kitchen_lights", 401)
async def test_put_light_state(hass_hue, hue_client):
async def test_put_light_state(hass, hass_hue, hue_client):
"""Test the setting of light states."""
await perform_put_test_on_ceiling_lights(hass_hue, hue_client)
@ -400,6 +400,21 @@ async def test_put_light_state(hass_hue, hue_client):
assert ceiling_lights.state == STATE_ON
assert ceiling_lights.attributes[light.ATTR_BRIGHTNESS] == 153
# update light state through api
await perform_put_light_state(
hass_hue,
hue_client,
"light.ceiling_lights",
True,
hue=4369,
saturation=127,
brightness=128,
)
assert (
hass.states.get("light.ceiling_lights").attributes[light.ATTR_BRIGHTNESS] == 129
)
# update light state through api
await perform_put_light_state(
hass_hue,
@ -411,6 +426,10 @@ async def test_put_light_state(hass_hue, hue_client):
brightness=123,
)
assert (
hass.states.get("light.ceiling_lights").attributes[light.ATTR_BRIGHTNESS] == 123
)
# go through api to get the state back
ceiling_json = await perform_get_light_state(
hue_client, "light.ceiling_lights", 200
@ -419,6 +438,25 @@ async def test_put_light_state(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,
hue=4369,
saturation=127,
brightness=255,
)
# go through api to get the state back
ceiling_json = await perform_get_light_state(
hue_client, "light.ceiling_lights", 200
)
assert ceiling_json["state"][HUE_API_STATE_BRI] == 254
assert ceiling_json["state"][HUE_API_STATE_HUE] == 4369
assert ceiling_json["state"][HUE_API_STATE_SAT] == 127
# Go through the API to turn it off
ceiling_result = await perform_put_light_state(
hass_hue, hue_client, "light.ceiling_lights", False
@ -454,7 +492,7 @@ async def test_put_light_state(hass_hue, hue_client):
assert kitchen_result.status == HTTP_NOT_FOUND
async def test_put_light_state_script(hass_hue, hue_client):
async def test_put_light_state_script(hass, hass_hue, hue_client):
"""Test the setting of script variables."""
# Turn the kitchen light off first
await hass_hue.services.async_call(
@ -464,9 +502,9 @@ async def test_put_light_state_script(hass_hue, hue_client):
blocking=True,
)
# Emulated hue converts 0-100% to 0-255.
# Emulated hue converts 0-100% to 0-254.
level = 23
brightness = round(level * 255 / 100)
brightness = round(level * 254 / 100)
script_result = await perform_put_light_state(
hass_hue, hue_client, "script.set_kitchen_light", True, brightness
@ -481,11 +519,15 @@ async def test_put_light_state_script(hass_hue, hue_client):
assert kitchen_light.state == "on"
assert kitchen_light.attributes[light.ATTR_BRIGHTNESS] == level
assert (
hass.states.get("light.kitchen_lights").attributes[light.ATTR_BRIGHTNESS] == 23
)
async def test_put_light_state_climate_set_temperature(hass_hue, hue_client):
"""Test setting climate temperature."""
brightness = 19
temperature = round(brightness / 255 * 100)
temperature = round(brightness / 254 * 100)
hvac_result = await perform_put_light_state(
hass_hue, hue_client, "climate.hvac", True, brightness
@ -517,9 +559,9 @@ async def test_put_light_state_media_player(hass_hue, hue_client):
blocking=True,
)
# Emulated hue converts 0.0-1.0 to 0-255.
# Emulated hue converts 0.0-1.0 to 0-254.
level = 0.25
brightness = round(level * 255)
brightness = round(level * 254)
mp_result = await perform_put_light_state(
hass_hue, hue_client, "media_player.walkman", True, brightness
@ -602,7 +644,7 @@ async def test_set_position_cover(hass_hue, hue_client):
assert cover_test.state == "closed"
level = 20
brightness = round(level / 100 * 255)
brightness = round(level / 100 * 254)
# Go through the API to open
cover_result = await perform_put_light_state(
@ -644,9 +686,9 @@ async def test_put_light_state_fan(hass_hue, hue_client):
blocking=True,
)
# Emulated hue converts 0-100% to 0-255.
# Emulated hue converts 0-100% to 0-254.
level = 43
brightness = round(level * 255 / 100)
brightness = round(level * 254 / 100)
fan_result = await perform_put_light_state(
hass_hue, hue_client, "fan.living_room_fan", True, brightness