async def async_turn_on(self, **kwargs): """Instruct the light to turn on.""" params = {} transition_time = None if ATTR_TRANSITION in kwargs: transition_time = int(kwargs[ATTR_TRANSITION]) * 10 brightness = kwargs.get(ATTR_BRIGHTNESS) if brightness is not None: if brightness > 254: brightness = 254 elif brightness < 0: brightness = 0 if ATTR_HS_COLOR in kwargs and self._light_control.can_set_color: params[ATTR_BRIGHTNESS] = brightness hue = int(kwargs[ATTR_HS_COLOR][0] * (65535 / 360)) sat = int(kwargs[ATTR_HS_COLOR][1] * (65279 / 100)) if brightness is None: params[ATTR_TRANSITION_TIME] = transition_time await self._api( self._light_control.set_hsb(hue, sat, **params)) return if ATTR_COLOR_TEMP in kwargs and (self._light_control.can_set_temp or self._light_control.can_set_color): temp = kwargs[ATTR_COLOR_TEMP] if temp > self.max_mireds: temp = self.max_mireds elif temp < self.min_mireds: temp = self.min_mireds if brightness is None: params[ATTR_TRANSITION_TIME] = transition_time # White Spectrum bulb if (self._light_control.can_set_temp and not self._light_control.can_set_color): await self._api( self._light_control.set_color_temp(temp, **params)) # Color bulb (CWS) # color_temp needs to be set with hue/saturation if self._light_control.can_set_color: params[ATTR_BRIGHTNESS] = brightness temp_k = color_util.color_temperature_mired_to_kelvin(temp) hs_color = color_util.color_temperature_to_hs(temp_k) hue = int(hs_color[0] * (65535 / 360)) sat = int(hs_color[1] * (65279 / 100)) await self._api( self._light_control.set_hsb(hue, sat, **params)) if brightness is not None: params[ATTR_TRANSITION_TIME] = transition_time await self._api( self._light_control.set_dimmer(brightness, **params)) else: await self._api( self._light_control.set_state(True))
def _set_light_mode(self, light_mode): _LOGGER.info("ArloNightLight: {} light mode {}".format(self._name, light_mode)) if light_mode is None: return # {'mode': 'rgb', 'rgb': {'red': 118, 'green': 255, 'blue': 91}} # {'mode': 'temperature', 'temperature': 2650} # {'mode': 'rainbow'} mode = light_mode.get("mode") if mode is None: return if mode == "rgb": rgb = light_mode.get("rgb") self._hs_color = color_util.color_RGB_to_hs( rgb.get("red"), rgb.get("green"), rgb.get("blue") ) self._effect = LIGHT_EFFECT_NONE elif mode == "temperature": temperature = light_mode.get("temperature") self._color_temp = color_util.color_temperature_kelvin_to_mired(temperature) self._hs_color = color_util.color_temperature_to_hs(temperature) self._effect = LIGHT_EFFECT_NONE elif mode == LIGHT_EFFECT_RAINBOW: self._effect = LIGHT_EFFECT_RAINBOW
async def async_handle_light_on_service(light, call): """Handle turning a light on. If brightness is set to 0, this service will turn the light off. """ params = dict(call.data["params"]) # Only process params once we processed brightness step if params and (ATTR_BRIGHTNESS_STEP in params or ATTR_BRIGHTNESS_STEP_PCT in params): brightness = light.brightness if light.is_on else 0 if ATTR_BRIGHTNESS_STEP in params: brightness += params.pop(ATTR_BRIGHTNESS_STEP) else: brightness += round( params.pop(ATTR_BRIGHTNESS_STEP_PCT) / 100 * 255) params[ATTR_BRIGHTNESS] = max(0, min(255, brightness)) preprocess_turn_on_alternatives(hass, params) if (not params or not light.is_on) or (params and ATTR_TRANSITION not in params): profiles.apply_default(light.entity_id, light.is_on, params) legacy_supported_color_modes = ( light._light_internal_supported_color_modes # pylint: disable=protected-access ) supported_color_modes = light.supported_color_modes # If a color temperature is specified, emulate it if not supported by the light if ATTR_COLOR_TEMP in params: if (supported_color_modes and ColorMode.COLOR_TEMP not in supported_color_modes and ColorMode.RGBWW in supported_color_modes): color_temp = params.pop(ATTR_COLOR_TEMP) brightness = params.get(ATTR_BRIGHTNESS, light.brightness) params[ ATTR_RGBWW_COLOR] = color_util.color_temperature_to_rgbww( color_temp, brightness, light.min_mireds, light.max_mireds) elif ColorMode.COLOR_TEMP not in legacy_supported_color_modes: color_temp = params.pop(ATTR_COLOR_TEMP) if color_supported(legacy_supported_color_modes): temp_k = color_util.color_temperature_mired_to_kelvin( color_temp) params[ATTR_HS_COLOR] = color_util.color_temperature_to_hs( temp_k) # If a color is specified, convert to the color space supported by the light # Backwards compatibility: Fall back to hs color if light.supported_color_modes # is not implemented if not supported_color_modes: if (rgb_color := params.pop(ATTR_RGB_COLOR, None)) is not None: params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) elif (xy_color := params.pop(ATTR_XY_COLOR, None)) is not None: params[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color)
def ct_to_hs(temp): """Convert color temperature (mireds) to hs.""" colorlist = list( color_util.color_temperature_to_hs( color_util.color_temperature_mired_to_kelvin(temp) ) ) return [int(val) for val in colorlist]
async def async_turn_on(self, **kwargs): """Instruct the light to turn on.""" params = {} transition_time = None if ATTR_TRANSITION in kwargs: transition_time = int(kwargs[ATTR_TRANSITION]) * 10 brightness = kwargs.get(ATTR_BRIGHTNESS) if brightness is not None: if brightness > 254: brightness = 254 elif brightness < 0: brightness = 0 if ATTR_HS_COLOR in kwargs and self._light_control.can_set_color: params[ATTR_BRIGHTNESS] = brightness hue = int(kwargs[ATTR_HS_COLOR][0] * (65535 / 360)) sat = int(kwargs[ATTR_HS_COLOR][1] * (65279 / 100)) if brightness is None: params[ATTR_TRANSITION_TIME] = transition_time await self._api(self._light_control.set_hsb(hue, sat, **params)) return if ATTR_COLOR_TEMP in kwargs and (self._light_control.can_set_temp or self._light_control.can_set_color): temp = kwargs[ATTR_COLOR_TEMP] if temp > self.max_mireds: temp = self.max_mireds elif temp < self.min_mireds: temp = self.min_mireds if brightness is None: params[ATTR_TRANSITION_TIME] = transition_time # White Spectrum bulb if (self._light_control.can_set_temp and not self._light_control.can_set_color): await self._api( self._light_control.set_color_temp(temp, **params)) # Color bulb (CWS) # color_temp needs to be set with hue/saturation if self._light_control.can_set_color: params[ATTR_BRIGHTNESS] = brightness temp_k = color_util.color_temperature_mired_to_kelvin(temp) hs_color = color_util.color_temperature_to_hs(temp_k) hue = int(hs_color[0] * (65535 / 360)) sat = int(hs_color[1] * (65279 / 100)) await self._api(self._light_control.set_hsb( hue, sat, **params)) if brightness is not None: params[ATTR_TRANSITION_TIME] = transition_time await self._api( self._light_control.set_dimmer(brightness, **params)) else: await self._api(self._light_control.set_state(True))
def async_update_state(self, new_state): """Update light after state change.""" # Handle State state = new_state.state if state == STATE_ON and self.char_on.value != 1: self.char_on.set_value(1) elif state == STATE_OFF and self.char_on.value != 0: self.char_on.set_value(0) # Handle Brightness if CHAR_BRIGHTNESS in self.chars: brightness = new_state.attributes.get(ATTR_BRIGHTNESS) if isinstance(brightness, (int, float)): brightness = round(brightness / 255 * 100, 0) # The homeassistant component might report its brightness as 0 but is # not off. But 0 is a special value in homekit. When you turn on a # homekit accessory it will try to restore the last brightness state # which will be the last value saved by char_brightness.set_value. # But if it is set to 0, HomeKit will update the brightness to 100 as # it thinks 0 is off. # # Therefore, if the the brightness is 0 and the device is still on, # the brightness is mapped to 1 otherwise the update is ignored in # order to avoid this incorrect behavior. if brightness == 0 and state == STATE_ON: brightness = 1 if self.char_brightness.value != brightness: self.char_brightness.set_value(brightness) # Handle color temperature if CHAR_COLOR_TEMPERATURE in self.chars: color_temperature = new_state.attributes.get(ATTR_COLOR_TEMP) if isinstance(color_temperature, (int, float)): color_temperature = round(color_temperature, 0) if self.char_color_temperature.value != color_temperature: self.char_color_temperature.set_value(color_temperature) # Handle Color if CHAR_SATURATION in self.chars and CHAR_HUE in self.chars: if ATTR_HS_COLOR in new_state.attributes: hue, saturation = new_state.attributes[ATTR_HS_COLOR] elif ATTR_COLOR_TEMP in new_state.attributes: hue, saturation = color_temperature_to_hs( color_temperature_mired_to_kelvin( new_state.attributes[ATTR_COLOR_TEMP])) else: hue, saturation = None, None if isinstance(hue, (int, float)) and isinstance(saturation, (int, float)): hue = round(hue, 0) saturation = round(saturation, 0) if hue != self.char_hue.value: self.char_hue.set_value(hue) if saturation != self.char_saturation.value: self.char_saturation.set_value(saturation)
async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the light.""" data = {ATTR_ON: True, ATTR_SEGMENT_ID: self._segment} if ATTR_COLOR_TEMP in kwargs: mireds = color_util.color_temperature_kelvin_to_mired( kwargs[ATTR_COLOR_TEMP]) data[ATTR_COLOR_PRIMARY] = tuple( map(int, color_util.color_temperature_to_rgb(mireds))) if ATTR_HS_COLOR in kwargs: hue, sat = kwargs[ATTR_HS_COLOR] data[ATTR_COLOR_PRIMARY] = color_util.color_hsv_to_RGB( hue, sat, 100) if ATTR_TRANSITION in kwargs: data[ATTR_TRANSITION] = kwargs[ATTR_TRANSITION] if ATTR_BRIGHTNESS in kwargs: data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS] if ATTR_EFFECT in kwargs: data[ATTR_EFFECT] = kwargs[ATTR_EFFECT] # Support for RGBW strips if self._rgbw and any(x in (ATTR_COLOR_TEMP, ATTR_HS_COLOR) for x in kwargs): data[ATTR_COLOR_PRIMARY] = color_util.color_rgb_to_rgbw( *data[ATTR_COLOR_PRIMARY]) try: await self.wled.light(**data) self._state = True if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] if ATTR_EFFECT in kwargs: self._effect = kwargs[ATTR_EFFECT] if ATTR_HS_COLOR in kwargs: self._color = kwargs[ATTR_HS_COLOR] if ATTR_COLOR_TEMP in kwargs: self._color = color_util.color_temperature_to_hs(mireds) except WLEDError: _LOGGER.error("An error occurred while turning on WLED light.") self._available = False self.async_schedule_update_ha_state()
def async_update_state(self, new_state): """Update light after state change.""" # Handle State state = new_state.state attributes = new_state.attributes self.char_on.set_value(int(state == STATE_ON)) # Handle Brightness if self.brightness_supported: brightness = attributes.get(ATTR_BRIGHTNESS) if isinstance(brightness, (int, float)): brightness = round(brightness / 255 * 100, 0) # The homeassistant component might report its brightness as 0 but is # not off. But 0 is a special value in homekit. When you turn on a # homekit accessory it will try to restore the last brightness state # which will be the last value saved by char_brightness.set_value. # But if it is set to 0, HomeKit will update the brightness to 100 as # it thinks 0 is off. # # Therefore, if the the brightness is 0 and the device is still on, # the brightness is mapped to 1 otherwise the update is ignored in # order to avoid this incorrect behavior. if brightness == 0 and state == STATE_ON: brightness = 1 self.char_brightness.set_value(brightness) # Handle Color - color must always be set before color temperature # or the iOS UI will not display it correctly. if self.color_supported: if ATTR_COLOR_TEMP in attributes: hue, saturation = color_temperature_to_hs( color_temperature_mired_to_kelvin( new_state.attributes[ATTR_COLOR_TEMP])) else: hue, saturation = attributes.get(ATTR_HS_COLOR, (None, None)) if isinstance(hue, (int, float)) and isinstance(saturation, (int, float)): self.char_hue.set_value(round(hue, 0)) self.char_saturation.set_value(round(saturation, 0)) # Handle color temperature if self.color_temp_supported: color_temp = attributes.get(ATTR_COLOR_TEMP) if isinstance(color_temp, (int, float)): self.char_color_temp.set_value(round(color_temp, 0))
def _light_internal_convert_color(self, color_mode: ColorMode | str) -> dict: data: dict[str, tuple] = {} if color_mode == ColorMode.HS and self.hs_color: hs_color = self.hs_color data[ATTR_HS_COLOR] = (round(hs_color[0], 3), round(hs_color[1], 3)) data[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color) data[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color) elif color_mode == ColorMode.XY and self.xy_color: xy_color = self.xy_color data[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color) data[ATTR_RGB_COLOR] = color_util.color_xy_to_RGB(*xy_color) data[ATTR_XY_COLOR] = (round(xy_color[0], 6), round(xy_color[1], 6)) elif color_mode == ColorMode.RGB and self.rgb_color: rgb_color = self.rgb_color data[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) data[ATTR_RGB_COLOR] = tuple(int(x) for x in rgb_color[0:3]) data[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color) elif color_mode == ColorMode.RGBW and self._light_internal_rgbw_color: rgbw_color = self._light_internal_rgbw_color rgb_color = color_util.color_rgbw_to_rgb(*rgbw_color) data[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) data[ATTR_RGB_COLOR] = tuple(int(x) for x in rgb_color[0:3]) data[ATTR_RGBW_COLOR] = tuple(int(x) for x in rgbw_color[0:4]) data[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color) elif color_mode == ColorMode.RGBWW and self.rgbww_color: rgbww_color = self.rgbww_color rgb_color = color_util.color_rgbww_to_rgb(*rgbww_color, self.min_mireds, self.max_mireds) data[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) data[ATTR_RGB_COLOR] = tuple(int(x) for x in rgb_color[0:3]) data[ATTR_RGBWW_COLOR] = tuple(int(x) for x in rgbww_color[0:5]) data[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color) elif color_mode == ColorMode.COLOR_TEMP and self.color_temp: hs_color = color_util.color_temperature_to_hs( color_util.color_temperature_mired_to_kelvin(self.color_temp)) data[ATTR_HS_COLOR] = (round(hs_color[0], 3), round(hs_color[1], 3)) data[ATTR_RGB_COLOR] = color_util.color_hs_to_RGB(*hs_color) data[ATTR_XY_COLOR] = color_util.color_hs_to_xy(*hs_color) return data
def _get_hs_from_properties(self): rgb = self._properties.get('rgb', None) color_mode = self._properties.get('color_mode', None) if not rgb or not color_mode: return None color_mode = int(color_mode) if color_mode == 2: # color temperature temp_in_k = mired_to_kelvin(self._color_temp) return color_util.color_temperature_to_hs(temp_in_k) if color_mode == 3: # hsv hue = int(self._properties.get('hue')) sat = int(self._properties.get('sat')) return (hue / 360 * 65536, sat / 100 * 255) rgb = int(rgb) blue = rgb & 0xff green = (rgb >> 8) & 0xff red = (rgb >> 16) & 0xff return color_util.color_RGB_to_hs(red, green, blue)
def _get_hs_from_properties(self): rgb = self._properties.get('rgb', None) color_mode = self._properties.get('color_mode', None) if not rgb or not color_mode: return None color_mode = int(color_mode) if color_mode == 2: # color temperature temp_in_k = mired_to_kelvin(self._color_temp) return color_util.color_temperature_to_hs(temp_in_k) if color_mode == 3: # hsv hue = int(self._properties.get('hue')) sat = int(self._properties.get('sat')) return (hue / 360 * 65536, sat / 100 * 255) rgb = int(rgb) blue = rgb & 0xff green = (rgb >> 8) & 0xff red = (rgb >> 16) & 0xff return color_util.color_RGB_to_hs(red, green, blue)
def _get_hs_from_properties(self): rgb = self._get_property("rgb") color_mode = self._get_property("color_mode") if not rgb or not color_mode: return None color_mode = int(color_mode) if color_mode == 2: # color temperature temp_in_k = mired_to_kelvin(self.color_temp) return color_util.color_temperature_to_hs(temp_in_k) if color_mode == 3: # hsv hue = int(self._get_property("hue")) sat = int(self._get_property("sat")) return (hue / 360 * 65536, sat / 100 * 255) rgb = int(rgb) blue = rgb & 0xFF green = (rgb >> 8) & 0xFF red = (rgb >> 16) & 0xFF return color_util.color_RGB_to_hs(red, green, blue)
async def async_turn_on(self, **kwargs): """Instruct the light to turn on.""" transition_time = None if ATTR_TRANSITION in kwargs: transition_time = int(kwargs[ATTR_TRANSITION]) * 10 dimmer_command = None if ATTR_BRIGHTNESS in kwargs: brightness = kwargs[ATTR_BRIGHTNESS] if brightness > 254: brightness = 254 elif brightness < 0: brightness = 0 dimmer_data = {ATTR_DIMMER: brightness, ATTR_TRANSITION_TIME: transition_time} dimmer_command = self._light_control.set_dimmer(**dimmer_data) transition_time = None else: dimmer_command = self._light_control.set_state(True) color_command = None if ATTR_HS_COLOR in kwargs and self._light_control.can_set_color: hue = int(kwargs[ATTR_HS_COLOR][0] * (self._light_control.max_hue / 360)) sat = int(kwargs[ATTR_HS_COLOR][1] * (self._light_control.max_saturation / 100)) color_data = {ATTR_HUE: hue, ATTR_SAT: sat, ATTR_TRANSITION_TIME: transition_time} color_command = self._light_control.set_hsb(**color_data) transition_time = None temp_command = None if ATTR_COLOR_TEMP in kwargs and (self._light_control.can_set_temp or self._light_control.can_set_color): temp = kwargs[ATTR_COLOR_TEMP] # White Spectrum bulb if self._light_control.can_set_temp: if temp > self.max_mireds: temp = self.max_mireds elif temp < self.min_mireds: temp = self.min_mireds temp_data = {ATTR_COLOR_TEMP: temp, ATTR_TRANSITION_TIME: transition_time} temp_command = self._light_control.set_color_temp(**temp_data) transition_time = None # Color bulb (CWS) # color_temp needs to be set with hue/saturation elif self._light_control.can_set_color: temp_k = color_util.color_temperature_mired_to_kelvin(temp) hs_color = color_util.color_temperature_to_hs(temp_k) hue = int(hs_color[0] * (self._light_control.max_hue / 360)) sat = int(hs_color[1] * (self._light_control.max_saturation / 100)) color_data = {ATTR_HUE: hue, ATTR_SAT: sat, ATTR_TRANSITION_TIME: transition_time} color_command = self._light_control.set_hsb(**color_data) transition_time = None # HSB can always be set, but color temp + brightness is bulb dependant command = dimmer_command if command is not None: command += color_command else: command = color_command if self._light_control.can_combine_commands: await self._api(command + temp_command) else: if temp_command is not None: await self._api(temp_command) if command is not None: await self._api(command)
async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the light.""" data = {ATTR_ON: True, ATTR_SEGMENT_ID: self._segment} if ATTR_COLOR_TEMP in kwargs: mireds = color_util.color_temperature_kelvin_to_mired( kwargs[ATTR_COLOR_TEMP] ) data[ATTR_COLOR_PRIMARY] = tuple( map(int, color_util.color_temperature_to_rgb(mireds)) ) if ATTR_HS_COLOR in kwargs: hue, sat = kwargs[ATTR_HS_COLOR] data[ATTR_COLOR_PRIMARY] = color_util.color_hsv_to_RGB(hue, sat, 100) if ATTR_TRANSITION in kwargs: data[ATTR_TRANSITION] = kwargs[ATTR_TRANSITION] if ATTR_BRIGHTNESS in kwargs: data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS] if ATTR_EFFECT in kwargs: data[ATTR_EFFECT] = kwargs[ATTR_EFFECT] # Support for RGBW strips, adds white value if self._rgbw and any( x in (ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_WHITE_VALUE) for x in kwargs ): # WLED cannot just accept a white value, it needs the color. # We use the last know color in case just the white value changes. if not any(x in (ATTR_COLOR_TEMP, ATTR_HS_COLOR) for x in kwargs): hue, sat = self._color data[ATTR_COLOR_PRIMARY] = color_util.color_hsv_to_RGB(hue, sat, 100) # Add requested or last known white value if ATTR_WHITE_VALUE in kwargs: data[ATTR_COLOR_PRIMARY] += (kwargs[ATTR_WHITE_VALUE],) else: data[ATTR_COLOR_PRIMARY] += (self._white_value,) try: await self.wled.light(**data) self._state = True if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] if ATTR_EFFECT in kwargs: self._effect = kwargs[ATTR_EFFECT] if ATTR_HS_COLOR in kwargs: self._color = kwargs[ATTR_HS_COLOR] if ATTR_COLOR_TEMP in kwargs: self._color = color_util.color_temperature_to_hs(mireds) if ATTR_WHITE_VALUE in kwargs: self._white_value = kwargs[ATTR_WHITE_VALUE] except WLEDError: _LOGGER.error("An error occurred while turning on WLED light.") self._available = False self.async_schedule_update_ha_state()
def ct_to_hs(temp): """Convert color temperature (mireds) to hs.""" colorlist = list( color_util.color_temperature_to_hs( color_util.color_temperature_mired_to_kelvin(temp))) return [int(val) for val in colorlist]
class Light(HomeAccessory): """Generate a Light accessory for a light entity. Currently supports: state, brightness, color temperature, rgb_color. """ def __init__(self, *args): """Initialize a new Light accessory object.""" super().__init__(*args, category=CATEGORY_LIGHTBULB) self.chars = [] self._event_timer = None self._pending_events = {} state = self.hass.states.get(self.entity_id) attributes = state.attributes self.color_modes = color_modes = ( attributes.get(ATTR_SUPPORTED_COLOR_MODES) or []) self._previous_color_mode = attributes.get(ATTR_COLOR_MODE) self.color_supported = color_supported(color_modes) self.color_temp_supported = color_temp_supported(color_modes) self.rgbw_supported = COLOR_MODE_RGBW in color_modes self.rgbww_supported = COLOR_MODE_RGBWW in color_modes self.white_supported = COLOR_MODE_WHITE in color_modes self.brightness_supported = brightness_supported(color_modes) if self.brightness_supported: self.chars.append(CHAR_BRIGHTNESS) if self.color_supported: self.chars.extend([CHAR_HUE, CHAR_SATURATION]) if self.color_temp_supported or COLOR_MODES_WITH_WHITES.intersection( self.color_modes): self.chars.append(CHAR_COLOR_TEMPERATURE) serv_light = self.add_preload_service(SERV_LIGHTBULB, self.chars) self.char_on = serv_light.configure_char(CHAR_ON, value=0) if self.brightness_supported: # Initial value is set to 100 because 0 is a special value (off). 100 is # an arbitrary non-zero value. It is updated immediately by async_update_state # to set to the correct initial value. self.char_brightness = serv_light.configure_char(CHAR_BRIGHTNESS, value=100) if CHAR_COLOR_TEMPERATURE in self.chars: self.min_mireds = math.floor( attributes.get(ATTR_MIN_MIREDS, DEFAULT_MIN_MIREDS)) self.max_mireds = math.ceil( attributes.get(ATTR_MAX_MIREDS, DEFAULT_MAX_MIREDS)) if not self.color_temp_supported and not self.rgbww_supported: self.max_mireds = self.min_mireds self.char_color_temp = serv_light.configure_char( CHAR_COLOR_TEMPERATURE, value=self.min_mireds, properties={ PROP_MIN_VALUE: self.min_mireds, PROP_MAX_VALUE: self.max_mireds, }, ) if self.color_supported: self.char_hue = serv_light.configure_char(CHAR_HUE, value=0) self.char_saturation = serv_light.configure_char(CHAR_SATURATION, value=75) self.async_update_state(state) serv_light.setter_callback = self._set_chars def _set_chars(self, char_values): _LOGGER.debug("Light _set_chars: %s", char_values) # Newest change always wins if CHAR_COLOR_TEMPERATURE in self._pending_events and ( CHAR_SATURATION in char_values or CHAR_HUE in char_values): del self._pending_events[CHAR_COLOR_TEMPERATURE] for char in (CHAR_HUE, CHAR_SATURATION): if char in self._pending_events and CHAR_COLOR_TEMPERATURE in char_values: del self._pending_events[char] self._pending_events.update(char_values) if self._event_timer: self._event_timer() self._event_timer = async_call_later(self.hass, CHANGE_COALESCE_TIME_WINDOW, self._async_send_events) @callback def _async_send_events(self, *_): """Process all changes at once.""" _LOGGER.debug("Coalesced _set_chars: %s", self._pending_events) char_values = self._pending_events self._pending_events = {} events = [] service = SERVICE_TURN_ON params = {ATTR_ENTITY_ID: self.entity_id} if CHAR_ON in char_values: if not char_values[CHAR_ON]: service = SERVICE_TURN_OFF events.append(f"Set state to {char_values[CHAR_ON]}") brightness_pct = None if CHAR_BRIGHTNESS in char_values: if char_values[CHAR_BRIGHTNESS] == 0: events[-1] = "Set state to 0" service = SERVICE_TURN_OFF else: brightness_pct = char_values[CHAR_BRIGHTNESS] events.append(f"brightness at {char_values[CHAR_BRIGHTNESS]}%") if service == SERVICE_TURN_OFF: self.async_call_service(DOMAIN, service, {ATTR_ENTITY_ID: self.entity_id}, ", ".join(events)) return # Handle white channels if CHAR_COLOR_TEMPERATURE in char_values: temp = char_values[CHAR_COLOR_TEMPERATURE] events.append(f"color temperature at {temp}") bright_val = round( ((brightness_pct or self.char_brightness.value) * 255) / 100) if self.color_temp_supported: params[ATTR_COLOR_TEMP] = temp elif self.rgbww_supported: params[ATTR_RGBWW_COLOR] = color_temperature_to_rgbww( temp, bright_val, self.min_mireds, self.max_mireds) elif self.rgbw_supported: params[ATTR_RGBW_COLOR] = (*(0, ) * 3, bright_val) elif self.white_supported: params[ATTR_WHITE] = bright_val elif CHAR_HUE in char_values or CHAR_SATURATION in char_values: hue_sat = ( char_values.get(CHAR_HUE, self.char_hue.value), char_values.get(CHAR_SATURATION, self.char_saturation.value), ) _LOGGER.debug("%s: Set hs_color to %s", self.entity_id, hue_sat) events.append(f"set color at {hue_sat}") params[ATTR_HS_COLOR] = hue_sat if (brightness_pct and ATTR_RGBWW_COLOR not in params and ATTR_RGBW_COLOR not in params): params[ATTR_BRIGHTNESS_PCT] = brightness_pct _LOGGER.debug("Calling light service with params: %s -> %s", char_values, params) self.async_call_service(DOMAIN, service, params, ", ".join(events)) @callback def async_update_state(self, new_state): """Update light after state change.""" # Handle State state = new_state.state attributes = new_state.attributes color_mode = attributes.get(ATTR_COLOR_MODE) self.char_on.set_value(int(state == STATE_ON)) color_mode_changed = self._previous_color_mode != color_mode self._previous_color_mode = color_mode # Handle Brightness if (self.brightness_supported and (brightness := attributes.get(ATTR_BRIGHTNESS)) is not None and isinstance(brightness, (int, float))): brightness = round(brightness / 255 * 100, 0) # The homeassistant component might report its brightness as 0 but is # not off. But 0 is a special value in homekit. When you turn on a # homekit accessory it will try to restore the last brightness state # which will be the last value saved by char_brightness.set_value. # But if it is set to 0, HomeKit will update the brightness to 100 as # it thinks 0 is off. # # Therefore, if the the brightness is 0 and the device is still on, # the brightness is mapped to 1 otherwise the update is ignored in # order to avoid this incorrect behavior. if brightness == 0 and state == STATE_ON: brightness = 1 self.char_brightness.set_value(brightness) if color_mode_changed: self.char_brightness.notify() # Handle Color - color must always be set before color temperature # or the iOS UI will not display it correctly. if self.color_supported: if color_temp := attributes.get(ATTR_COLOR_TEMP): hue, saturation = color_temperature_to_hs( color_temperature_mired_to_kelvin(color_temp)) elif color_mode == COLOR_MODE_WHITE: hue, saturation = 0, 0 else: hue, saturation = attributes.get(ATTR_HS_COLOR, (None, None)) if isinstance(hue, (int, float)) and isinstance(saturation, (int, float)): self.char_hue.set_value(round(hue, 0)) self.char_saturation.set_value(round(saturation, 0)) if color_mode_changed: # If the color temp changed, be sure to force the color to update self.char_hue.notify() self.char_saturation.notify()
async def async_turn_on(self, **kwargs): """Forward the turn_on command to all lights in the light group.""" data = {ATTR_ENTITY_ID: self._entity_ids} emulate_color_temp_entity_ids = [] if ATTR_BRIGHTNESS in kwargs: data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS] if ATTR_HS_COLOR in kwargs: data[ATTR_HS_COLOR] = kwargs[ATTR_HS_COLOR] if ATTR_RGB_COLOR in kwargs: data[ATTR_RGB_COLOR] = kwargs[ATTR_RGB_COLOR] if ATTR_RGBW_COLOR in kwargs: data[ATTR_RGBW_COLOR] = kwargs[ATTR_RGBW_COLOR] if ATTR_RGBWW_COLOR in kwargs: data[ATTR_RGBWW_COLOR] = kwargs[ATTR_RGBWW_COLOR] if ATTR_XY_COLOR in kwargs: data[ATTR_XY_COLOR] = kwargs[ATTR_XY_COLOR] if ATTR_COLOR_TEMP in kwargs: data[ATTR_COLOR_TEMP] = kwargs[ATTR_COLOR_TEMP] # Create a new entity list to mutate updated_entities = list(self._entity_ids) # Walk through initial entity ids, split entity lists by support for entity_id in self._entity_ids: state = self.hass.states.get(entity_id) if not state: continue support = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES) # Only pass color temperature to supported entity_ids if color_supported( support) and not color_temp_supported(support): emulate_color_temp_entity_ids.append(entity_id) updated_entities.remove(entity_id) data[ATTR_ENTITY_ID] = updated_entities if ATTR_WHITE_VALUE in kwargs: data[ATTR_WHITE_VALUE] = kwargs[ATTR_WHITE_VALUE] if ATTR_EFFECT in kwargs: data[ATTR_EFFECT] = kwargs[ATTR_EFFECT] if ATTR_TRANSITION in kwargs: data[ATTR_TRANSITION] = kwargs[ATTR_TRANSITION] if ATTR_FLASH in kwargs: data[ATTR_FLASH] = kwargs[ATTR_FLASH] if not emulate_color_temp_entity_ids: await self.hass.services.async_call( light.DOMAIN, light.SERVICE_TURN_ON, data, blocking=True, context=self._context, ) return emulate_color_temp_data = data.copy() temp_k = color_util.color_temperature_mired_to_kelvin( emulate_color_temp_data[ATTR_COLOR_TEMP]) hs_color = color_util.color_temperature_to_hs(temp_k) emulate_color_temp_data[ATTR_HS_COLOR] = hs_color del emulate_color_temp_data[ATTR_COLOR_TEMP] emulate_color_temp_data[ATTR_ENTITY_ID] = emulate_color_temp_entity_ids await asyncio.gather( self.hass.services.async_call( light.DOMAIN, light.SERVICE_TURN_ON, data, blocking=True, context=self._context, ), self.hass.services.async_call( light.DOMAIN, light.SERVICE_TURN_ON, emulate_color_temp_data, blocking=True, context=self._context, ), )
async def async_turn_on(self, **kwargs): """Instruct the light to turn on.""" transition_time = None if ATTR_TRANSITION in kwargs: transition_time = int(kwargs[ATTR_TRANSITION]) * 10 dimmer_command = None if ATTR_BRIGHTNESS in kwargs: brightness = kwargs[ATTR_BRIGHTNESS] brightness = min(brightness, 254) dimmer_data = { ATTR_DIMMER: brightness, ATTR_TRANSITION_TIME: transition_time, } dimmer_command = self._device_control.set_dimmer(**dimmer_data) transition_time = None else: dimmer_command = self._device_control.set_state(True) color_command = None if ATTR_HS_COLOR in kwargs and self._device_control.can_set_color: hue = int(kwargs[ATTR_HS_COLOR][0] * (self._device_control.max_hue / 360)) sat = int(kwargs[ATTR_HS_COLOR][1] * (self._device_control.max_saturation / 100)) color_data = { ATTR_HUE: hue, ATTR_SAT: sat, ATTR_TRANSITION_TIME: transition_time, } color_command = self._device_control.set_hsb(**color_data) transition_time = None temp_command = None if ATTR_COLOR_TEMP in kwargs and (self._device_control.can_set_temp or self._device_control.can_set_color): temp = kwargs[ATTR_COLOR_TEMP] # White Spectrum bulb if self._device_control.can_set_temp: if temp > self.max_mireds: temp = self.max_mireds elif temp < self.min_mireds: temp = self.min_mireds temp_data = { ATTR_COLOR_TEMP: temp, ATTR_TRANSITION_TIME: transition_time, } temp_command = self._device_control.set_color_temp(**temp_data) transition_time = None # Color bulb (CWS) # color_temp needs to be set with hue/saturation elif self._device_control.can_set_color: temp_k = color_util.color_temperature_mired_to_kelvin(temp) hs_color = color_util.color_temperature_to_hs(temp_k) hue = int(hs_color[0] * (self._device_control.max_hue / 360)) sat = int(hs_color[1] * (self._device_control.max_saturation / 100)) color_data = { ATTR_HUE: hue, ATTR_SAT: sat, ATTR_TRANSITION_TIME: transition_time, } color_command = self._device_control.set_hsb(**color_data) transition_time = None # HSB can always be set, but color temp + brightness is bulb dependent command = dimmer_command if command is not None: command += color_command else: command = color_command if self._device_control.can_combine_commands: await self._api(command + temp_command) else: if temp_command is not None: await self._api(temp_command) if command is not None: await self._api(command)
def _hs_color_for_temperature(self, temperature: int) -> Tuple[float, float]: temp_k = color_util.color_temperature_mired_to_kelvin(temperature) hs_color = color_util.color_temperature_to_hs(temp_k) return hs_color