From 1ef55ca180e0b2b6cd9310088e8ede661771fb99 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Sun, 11 Dec 2022 16:54:57 -0800 Subject: [PATCH] Use Kelvin instead of Mired, default since core=2022.11 (#375) * Use Kelvin instead of Mired, default since core=2022.11 * Fix attributes * Use ATTR_COLOR_TEMP_KELVIN in tests * use kelvin in tests * no duplicate * Round to nearest 5 --- custom_components/adaptive_lighting/switch.py | 40 ++++++++--------- tests/test_switch.py | 45 ++++++++++++++----- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index e2dee95f..acd483a2 100755 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -21,7 +21,7 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS_STEP, ATTR_BRIGHTNESS_STEP_PCT, ATTR_COLOR_NAME, - ATTR_COLOR_TEMP, + ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, ATTR_KELVIN, ATTR_RGB_COLOR, @@ -85,7 +85,6 @@ from homeassistant.helpers.template import area_entities from homeassistant.util import slugify from homeassistant.util.color import ( color_RGB_to_xy, - color_temperature_kelvin_to_mired, color_temperature_to_rgb, color_xy_to_hs, ) @@ -155,12 +154,12 @@ SCAN_INTERVAL = timedelta(seconds=10) # Consider it a significant change when attribute changes more than BRIGHTNESS_CHANGE = 25 # ≈10% of total range -COLOR_TEMP_CHANGE = 20 # ≈5% of total range +COLOR_TEMP_CHANGE = 100 # ≈3% of total range (2000-6500) RGB_REDMEAN_CHANGE = 80 # ≈10% of total range COLOR_ATTRS = { # Should ATTR_PROFILE be in here? ATTR_COLOR_NAME, - ATTR_COLOR_TEMP, + ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, ATTR_KELVIN, ATTR_RGB_COLOR, @@ -228,7 +227,7 @@ def _split_service_data(service_data, adapt_brightness, adapt_color): if adapt_brightness: service_data_brightness = service_data.copy() service_data_brightness.pop(ATTR_RGB_COLOR, None) - service_data_brightness.pop(ATTR_COLOR_TEMP, None) + service_data_brightness.pop(ATTR_COLOR_TEMP_KELVIN, None) service_datas.append(service_data_brightness) if not service_datas: # neither adapt_brightness nor adapt_color @@ -491,11 +490,11 @@ def _attributes_have_changed( if ( adapt_color - and ATTR_COLOR_TEMP in old_attributes - and ATTR_COLOR_TEMP in new_attributes + and ATTR_COLOR_TEMP_KELVIN in old_attributes + and ATTR_COLOR_TEMP_KELVIN in new_attributes ): - last_color_temp = old_attributes[ATTR_COLOR_TEMP] - current_color_temp = new_attributes[ATTR_COLOR_TEMP] + last_color_temp = old_attributes[ATTR_COLOR_TEMP_KELVIN] + current_color_temp = new_attributes[ATTR_COLOR_TEMP_KELVIN] if abs(current_color_temp - last_color_temp) > COLOR_TEMP_CHANGE: _LOGGER.debug( "Color temperature of '%s' significantly changed from %s to %s with" @@ -530,7 +529,8 @@ def _attributes_have_changed( ATTR_RGB_COLOR in old_attributes and ATTR_RGB_COLOR not in new_attributes ) switched_to_rgb_color = ( - ATTR_COLOR_TEMP in old_attributes and ATTR_COLOR_TEMP not in new_attributes + ATTR_COLOR_TEMP_KELVIN in old_attributes + and ATTR_COLOR_TEMP_KELVIN not in new_attributes ) if switched_color_temp or switched_to_rgb_color: # Light switched from RGB mode to color_temp or visa versa @@ -824,10 +824,11 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity): ): _LOGGER.debug("%s: Setting color_temp of light %s", self._name, light) attributes = self.hass.states.get(light).attributes - min_mireds, max_mireds = attributes["min_mireds"], attributes["max_mireds"] - color_temp_mired = self._settings["color_temp_mired"] - color_temp_mired = max(min(color_temp_mired, max_mireds), min_mireds) - service_data[ATTR_COLOR_TEMP] = color_temp_mired + min_kelvin = attributes["min_color_temp_kelvin"] + max_kelvin = attributes["max_color_temp_kelvin"] + color_temp_kelvin = self._settings["color_temp_kelvin"] + color_temp_kelvin = max(min(color_temp_kelvin, max_kelvin), min_kelvin) + service_data[ATTR_COLOR_TEMP_KELVIN] = color_temp_kelvin elif "color" in features and adapt_color: _LOGGER.debug("%s: Setting rgb_color of light %s", self._name, light) service_data[ATTR_RGB_COLOR] = self._settings["rgb_color"] @@ -1229,16 +1230,17 @@ class SunLightSettings: percent = 1 + percent return (delta_brightness * percent) + self.min_brightness - def calc_color_temp_kelvin(self, percent: float) -> float: + def calc_color_temp_kelvin(self, percent: float) -> int: """Calculate the color temperature in Kelvin.""" if percent > 0: delta = self.max_color_temp - self.min_color_temp - return (delta * percent) + self.min_color_temp + ct = (delta * percent) + self.min_color_temp + return 5 * round(ct / 5) # round to nearest 5 return self.min_color_temp def get_settings( self, is_sleep, transition - ) -> dict[str, float | tuple[float, float] | tuple[float, float, float]]: + ) -> dict[str, float | int | tuple[float, float] | tuple[float, float, float]]: """Get all light settings. Calculating all values takes <0.5ms. @@ -1257,13 +1259,11 @@ class SunLightSettings: rgb_color: tuple[float, float, float] = color_temperature_to_rgb( color_temp_kelvin ) - color_temp_mired: float = color_temperature_kelvin_to_mired(color_temp_kelvin) xy_color: tuple[float, float] = color_RGB_to_xy(*rgb_color) hs_color: tuple[float, float] = color_xy_to_hs(*xy_color) return { "brightness_pct": brightness_pct, "color_temp_kelvin": color_temp_kelvin, - "color_temp_mired": color_temp_mired, "rgb_color": rgb_color, "xy_color": xy_color, "hs_color": hs_color, @@ -1397,7 +1397,7 @@ class TurnOnOffListener: # settings the light will be later *or* the second event might indicate a # final state. The latter case happens for example when a light was # called with a color_temp outside of its range (and HA reports the - # incorrect 'min_mireds' and 'max_mireds', which happens e.g., for + # incorrect 'min_kelvin' and 'max_kelvin', which happens e.g., for # Philips Hue White GU10 Bluetooth lights). old_state: list[State] | None = self.last_state_change.get(entity_id) if ( diff --git a/tests/test_switch.py b/tests/test_switch.py index 312cf7c6..59c9245d 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -41,7 +41,7 @@ from homeassistant.components.demo.light import DemoLight from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, - ATTR_COLOR_TEMP, + ATTR_COLOR_TEMP_KELVIN, ATTR_RGB_COLOR, ) from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN @@ -62,6 +62,7 @@ from homeassistant.const import ( from homeassistant.core import Context, State from homeassistant.helpers import entity_registry from homeassistant.setup import async_setup_component +from homeassistant.util.color import color_temperature_mired_to_kelvin import homeassistant.util.dt as dt_util import pytest @@ -338,7 +339,10 @@ async def test_light_settings(hass): state.entity_id ] assert state.attributes[ATTR_BRIGHTNESS] == last_service_data[ATTR_BRIGHTNESS] - assert state.attributes[ATTR_COLOR_TEMP] == last_service_data[ATTR_COLOR_TEMP] + assert ( + state.attributes[ATTR_COLOR_TEMP_KELVIN] + == last_service_data[ATTR_COLOR_TEMP_KELVIN] + ) # Turn off "sleep mode" await hass.services.async_call( @@ -371,7 +375,10 @@ async def test_light_settings(hass): last_service_data = switch.turn_on_off_listener.last_service_data[ state.entity_id ] - assert state.attributes[ATTR_COLOR_TEMP] == last_service_data[ATTR_COLOR_TEMP] + assert ( + state.attributes[ATTR_COLOR_TEMP_KELVIN] + == last_service_data[ATTR_COLOR_TEMP_KELVIN] + ) # At sunset the brightness should be max and color_temp at the smallest value light_states = await patch_time_and_get_updated_states(sunset) @@ -481,7 +488,9 @@ async def test_manual_control(hass): return (light._brightness + 100) % 255 def increased_color_temp(): - return max((light._ct + 100) % light.max_mireds, light.min_mireds) + return max( + (light._ct + 100) % light.max_color_temp_kelvin, light.min_color_temp_kelvin + ) # Nothing is manually controlled await update() @@ -520,7 +529,13 @@ async def test_manual_control(hass): await switch.adapt_brightness_switch.async_turn_off() await turn_light(True, brightness=increased_brightness()) assert not manual_control[ENTITY_LIGHT] - await turn_light(True, color_temp=(light._ct + 100) % 500) + mired_range = (light.min_color_temp_kelvin, light.max_color_temp_kelvin) + kelvin_range = ( + color_temperature_mired_to_kelvin(mired_range[1]), + color_temperature_mired_to_kelvin(mired_range[0]), + ) + ptp_kelvin = kelvin_range[1] - kelvin_range[0] + await turn_light(True, color_temp_kelvin=(light._ct + 100) % ptp_kelvin) assert manual_control[ENTITY_LIGHT] await switch.adapt_brightness_switch.async_turn_on() # turn on again @@ -573,7 +588,9 @@ async def test_apply_service(hass): return (light._brightness + 100) % 255 def increased_color_temp(): - return max((light._ct + 100) % light.max_mireds, light.min_mireds) + return max( + (light._ct + 100) % light.max_color_temp_kelvin, light.min_color_temp_kelvin + ) async def change_light(): await hass.services.async_call( @@ -582,7 +599,7 @@ async def test_apply_service(hass): { ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: increased_brightness(), - ATTR_COLOR_TEMP: increased_color_temp(), + ATTR_COLOR_TEMP_KELVIN: increased_color_temp(), }, blocking=True, ) @@ -613,7 +630,7 @@ async def test_apply_service(hass): await apply(adapt_color=True, adapt_brightness=False) new_state = hass.states.get(entity_id).attributes assert old_state[ATTR_BRIGHTNESS] == new_state[ATTR_BRIGHTNESS] - assert old_state[ATTR_COLOR_TEMP] != new_state[ATTR_COLOR_TEMP] + assert old_state[ATTR_COLOR_TEMP_KELVIN] != new_state[ATTR_COLOR_TEMP_KELVIN] # Test only changing brightness await change_light() @@ -621,7 +638,7 @@ async def test_apply_service(hass): await apply(adapt_color=False, adapt_brightness=True) new_state = hass.states.get(entity_id).attributes assert old_state[ATTR_BRIGHTNESS] != new_state[ATTR_BRIGHTNESS] - assert old_state[ATTR_COLOR_TEMP] == new_state[ATTR_COLOR_TEMP] + assert old_state[ATTR_COLOR_TEMP_KELVIN] == new_state[ATTR_COLOR_TEMP_KELVIN] async def test_switch_off_on_off(hass): @@ -733,11 +750,15 @@ def test_is_our_context(): def test_attributes_have_changed(): """Test _attributes_have_changed function.""" - attributes_1 = {ATTR_BRIGHTNESS: 1, ATTR_RGB_COLOR: (0, 0, 0), ATTR_COLOR_TEMP: 100} + attributes_1 = { + ATTR_BRIGHTNESS: 1, + ATTR_RGB_COLOR: (0, 0, 0), + ATTR_COLOR_TEMP_KELVIN: 100, + } attributes_2 = { ATTR_BRIGHTNESS: 100, ATTR_RGB_COLOR: (255, 0, 0), - ATTR_COLOR_TEMP: 300, + ATTR_COLOR_TEMP_KELVIN: 300, } kwargs = dict( light="light.test", @@ -756,7 +777,7 @@ def test_attributes_have_changed(): ) # Switch from rgb_color to color_temp assert _attributes_have_changed( - old_attributes={ATTR_BRIGHTNESS: 1, ATTR_COLOR_TEMP: 100}, + old_attributes={ATTR_BRIGHTNESS: 1, ATTR_COLOR_TEMP_KELVIN: 100}, new_attributes={ATTR_BRIGHTNESS: 1, ATTR_RGB_COLOR: (0, 0, 0)}, **kwargs, )