From bb5484edacba9f3c8818e682f54e7dc2b470abc6 Mon Sep 17 00:00:00 2001 From: Niklas Morberg Date: Thu, 5 Apr 2018 18:06:23 +0200 Subject: [PATCH] Support color temperature in Homekit (#13658) * Add support for color temperature * Add test for color temp --- homeassistant/components/homekit/const.py | 1 + .../components/homekit/type_lights.py | 39 +++++++++++++++++-- tests/components/homekit/test_type_lights.py | 26 ++++++++++++- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 676f83bf8e8..d1c3d84b517 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -47,6 +47,7 @@ SERV_WINDOW_COVERING = 'WindowCovering' # #### Characteristics #### CHAR_BRIGHTNESS = 'Brightness' # Int | [0, 100] +CHAR_COLOR_TEMPERATURE = 'ColorTemperature' CHAR_COOLING_THRESHOLD_TEMPERATURE = 'CoolingThresholdTemperature' CHAR_CURRENT_HEATING_COOLING = 'CurrentHeatingCoolingState' CHAR_CURRENT_POSITION = 'CurrentPosition' diff --git a/homeassistant/components/homekit/type_lights.py b/homeassistant/components/homekit/type_lights.py index 45ed9405a2a..018d3cd2e74 100644 --- a/homeassistant/components/homekit/type_lights.py +++ b/homeassistant/components/homekit/type_lights.py @@ -2,13 +2,14 @@ import logging from homeassistant.components.light import ( - ATTR_HS_COLOR, ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, SUPPORT_COLOR) + ATTR_HS_COLOR, ATTR_COLOR_TEMP, ATTR_BRIGHTNESS, ATTR_MIN_MIREDS, + ATTR_MAX_MIREDS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_BRIGHTNESS) from homeassistant.const import ATTR_SUPPORTED_FEATURES, STATE_ON, STATE_OFF from . import TYPES from .accessories import HomeAccessory, add_preload_service from .const import ( - CATEGORY_LIGHT, SERV_LIGHTBULB, + CATEGORY_LIGHT, SERV_LIGHTBULB, CHAR_COLOR_TEMPERATURE, CHAR_BRIGHTNESS, CHAR_HUE, CHAR_ON, CHAR_SATURATION) _LOGGER = logging.getLogger(__name__) @@ -20,7 +21,7 @@ RGB_COLOR = 'rgb_color' class Light(HomeAccessory): """Generate a Light accessory for a light entity. - Currently supports: state, brightness, rgb_color. + Currently supports: state, brightness, color temperature, rgb_color. """ def __init__(self, hass, entity_id, name, **kwargs): @@ -31,7 +32,7 @@ class Light(HomeAccessory): self.entity_id = entity_id self._flag = {CHAR_ON: False, CHAR_BRIGHTNESS: False, CHAR_HUE: False, CHAR_SATURATION: False, - RGB_COLOR: False} + CHAR_COLOR_TEMPERATURE: False, RGB_COLOR: False} self._state = 0 self.chars = [] @@ -39,6 +40,8 @@ class Light(HomeAccessory): .attributes.get(ATTR_SUPPORTED_FEATURES) if self._features & SUPPORT_BRIGHTNESS: self.chars.append(CHAR_BRIGHTNESS) + if self._features & SUPPORT_COLOR_TEMP: + self.chars.append(CHAR_COLOR_TEMPERATURE) if self._features & SUPPORT_COLOR: self.chars.append(CHAR_HUE) self.chars.append(CHAR_SATURATION) @@ -55,6 +58,18 @@ class Light(HomeAccessory): .get_characteristic(CHAR_BRIGHTNESS) self.char_brightness.setter_callback = self.set_brightness self.char_brightness.value = 0 + if CHAR_COLOR_TEMPERATURE in self.chars: + self.char_color_temperature = serv_light \ + .get_characteristic(CHAR_COLOR_TEMPERATURE) + self.char_color_temperature.setter_callback = \ + self.set_color_temperature + min_mireds = self.hass.states.get(self.entity_id) \ + .attributes.get(ATTR_MIN_MIREDS, 153) + max_mireds = self.hass.states.get(self.entity_id) \ + .attributes.get(ATTR_MAX_MIREDS, 500) + self.char_color_temperature.override_properties({ + 'minValue': min_mireds, 'maxValue': max_mireds}) + self.char_color_temperature.value = min_mireds if CHAR_HUE in self.chars: self.char_hue = serv_light.get_characteristic(CHAR_HUE) self.char_hue.setter_callback = self.set_hue @@ -90,6 +105,13 @@ class Light(HomeAccessory): else: self.hass.components.light.turn_off(self.entity_id) + def set_color_temperature(self, value): + """Set color temperature if call came from HomeKit.""" + _LOGGER.debug('%s: Set color temp to %s', self.entity_id, value) + self._flag[CHAR_COLOR_TEMPERATURE] = True + self.char_color_temperature.set_value(value, should_callback=False) + self.hass.components.light.turn_on(self.entity_id, color_temp=value) + def set_saturation(self, value): """Set saturation if call came from HomeKit.""" _LOGGER.debug('%s: Set saturation to %d', self.entity_id, value) @@ -141,6 +163,15 @@ class Light(HomeAccessory): should_callback=False) self._flag[CHAR_BRIGHTNESS] = False + # Handle color temperature + if CHAR_COLOR_TEMPERATURE in self.chars: + color_temperature = new_state.attributes.get(ATTR_COLOR_TEMP) + if not self._flag[CHAR_COLOR_TEMPERATURE] \ + and isinstance(color_temperature, int): + self.char_color_temperature.set_value(color_temperature, + should_callback=False) + self._flag[CHAR_COLOR_TEMPERATURE] = False + # Handle Color if CHAR_SATURATION in self.chars and CHAR_HUE in self.chars: hue, saturation = new_state.attributes.get( diff --git a/tests/components/homekit/test_type_lights.py b/tests/components/homekit/test_type_lights.py index ee1900fd7c5..1cfb926c4ce 100644 --- a/tests/components/homekit/test_type_lights.py +++ b/tests/components/homekit/test_type_lights.py @@ -4,8 +4,8 @@ import unittest from homeassistant.core import callback from homeassistant.components.homekit.type_lights import Light from homeassistant.components.light import ( - DOMAIN, ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, ATTR_HS_COLOR, - SUPPORT_BRIGHTNESS, SUPPORT_COLOR) + DOMAIN, ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, ATTR_COLOR_TEMP, + ATTR_HS_COLOR, SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_COLOR) from homeassistant.const import ( ATTR_DOMAIN, ATTR_ENTITY_ID, ATTR_SERVICE, ATTR_SERVICE_DATA, ATTR_SUPPORTED_FEATURES, EVENT_CALL_SERVICE, SERVICE_TURN_ON, @@ -118,6 +118,28 @@ class TestHomekitLights(unittest.TestCase): self.assertEqual(self.events[2].data[ATTR_DOMAIN], DOMAIN) self.assertEqual(self.events[2].data[ATTR_SERVICE], SERVICE_TURN_OFF) + def test_light_color_temperature(self): + """Test light with color temperature.""" + entity_id = 'light.demo' + self.hass.states.set(entity_id, STATE_ON, { + ATTR_SUPPORTED_FEATURES: SUPPORT_COLOR_TEMP, + ATTR_COLOR_TEMP: 190}) + acc = Light(self.hass, entity_id, 'Light', aid=2) + self.assertEqual(acc.char_color_temperature.value, 153) + + acc.run() + self.hass.block_till_done() + self.assertEqual(acc.char_color_temperature.value, 190) + + # Set from HomeKit + acc.char_color_temperature.set_value(250) + self.hass.block_till_done() + self.assertEqual(self.events[0].data[ATTR_DOMAIN], DOMAIN) + self.assertEqual(self.events[0].data[ATTR_SERVICE], SERVICE_TURN_ON) + self.assertEqual( + self.events[0].data[ATTR_SERVICE_DATA], { + ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 250}) + def test_light_rgb_color(self): """Test light with rgb_color.""" entity_id = 'light.demo'