Free color selection for Fritz!Smarthome lights (#66213)

* Fritz light free color selection

* Use setcolor as fallback

* better debug log message

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>

* change if-clause

Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com>
This commit is contained in:
Lars 2022-05-25 09:28:36 +02:00 committed by GitHub
parent 1e4690626f
commit 804c888098
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 11 deletions

View File

@ -3,6 +3,8 @@ from __future__ import annotations
from typing import Any from typing import Any
from requests.exceptions import HTTPError
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP, ATTR_COLOR_TEMP,
@ -21,6 +23,7 @@ from .const import (
COLOR_TEMP_MODE, COLOR_TEMP_MODE,
CONF_COORDINATOR, CONF_COORDINATOR,
DOMAIN as FRITZBOX_DOMAIN, DOMAIN as FRITZBOX_DOMAIN,
LOGGER,
) )
from .coordinator import FritzboxDataUpdateCoordinator from .coordinator import FritzboxDataUpdateCoordinator
@ -135,16 +138,33 @@ class FritzboxLight(FritzBoxEntity, LightEntity):
level = kwargs[ATTR_BRIGHTNESS] level = kwargs[ATTR_BRIGHTNESS]
await self.hass.async_add_executor_job(self.device.set_level, level) await self.hass.async_add_executor_job(self.device.set_level, level)
if kwargs.get(ATTR_HS_COLOR) is not None: if kwargs.get(ATTR_HS_COLOR) is not None:
hass_hue = int(kwargs[ATTR_HS_COLOR][0]) # Try setunmappedcolor first. This allows free color selection,
hass_saturation = round(kwargs[ATTR_HS_COLOR][1] * 255.0 / 100.0) # but we don't know if its supported by all devices.
# find supported hs values closest to what user selected try:
hue = min(self._supported_hs.keys(), key=lambda x: abs(x - hass_hue)) # HA gives 0..360 for hue, fritz light only supports 0..359
saturation = min( unmapped_hue = int(kwargs[ATTR_HS_COLOR][0] % 360)
self._supported_hs[hue], key=lambda x: abs(x - hass_saturation) unmapped_saturation = round(kwargs[ATTR_HS_COLOR][1] * 255.0 / 100.0)
) await self.hass.async_add_executor_job(
await self.hass.async_add_executor_job( self.device.set_unmapped_color, (unmapped_hue, unmapped_saturation)
self.device.set_color, (hue, saturation) )
) # This will raise 400 BAD REQUEST if the setunmappedcolor is not available
except HTTPError as err:
if err.response.status_code != 400:
raise
LOGGER.debug(
"fritzbox does not support method 'setunmappedcolor', fallback to 'setcolor'"
)
# find supported hs values closest to what user selected
hue = min(
self._supported_hs.keys(), key=lambda x: abs(x - unmapped_hue)
)
saturation = min(
self._supported_hs[hue],
key=lambda x: abs(x - unmapped_saturation),
)
await self.hass.async_add_executor_job(
self.device.set_color, (hue, saturation)
)
if kwargs.get(ATTR_COLOR_TEMP) is not None: if kwargs.get(ATTR_COLOR_TEMP) is not None:
kelvin = color.color_temperature_kelvin_to_mired(kwargs[ATTR_COLOR_TEMP]) kelvin = color.color_temperature_kelvin_to_mired(kwargs[ATTR_COLOR_TEMP])

View File

@ -113,7 +113,34 @@ async def test_turn_on_color(hass: HomeAssistant, fritz: Mock):
assert await setup_config_entry( assert await setup_config_entry(
hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz
) )
assert await hass.services.async_call(
DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_BRIGHTNESS: 100, ATTR_HS_COLOR: (100, 70)},
True,
)
assert device.set_state_on.call_count == 1
assert device.set_level.call_count == 1
assert device.set_unmapped_color.call_count == 1
async def test_turn_on_color_unsupported_api_method(hass: HomeAssistant, fritz: Mock):
"""Test turn device on in mapped color mode if unmapped is not supported."""
device = FritzDeviceLightMock()
device.get_color_temps.return_value = [2700, 6500]
device.get_colors.return_value = {
"Red": [("100", "70", "10"), ("100", "50", "10"), ("100", "30", "10")]
}
mockresponse = Mock()
mockresponse.status_code = 400
error = HTTPError("Bad Request")
error.response = mockresponse
device.set_unmapped_color.side_effect = error
assert await setup_config_entry(
hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz
)
assert await hass.services.async_call( assert await hass.services.async_call(
DOMAIN, DOMAIN,
SERVICE_TURN_ON, SERVICE_TURN_ON,
@ -135,7 +162,6 @@ async def test_turn_off(hass: HomeAssistant, fritz: Mock):
assert await setup_config_entry( assert await setup_config_entry(
hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz
) )
assert await hass.services.async_call( assert await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True
) )