def preprocess_turn_on_alternatives(params): """Process extra data for turn light on request.""" profile = Profiles.get(params.pop(ATTR_PROFILE, None)) if profile is not None: params.setdefault(ATTR_XY_COLOR, profile[:2]) params.setdefault(ATTR_BRIGHTNESS, profile[2]) color_name = params.pop(ATTR_COLOR_NAME, None) if color_name is not None: try: params[ATTR_RGB_COLOR] = color_util.color_name_to_rgb(color_name) except ValueError: _LOGGER.warning("Got unknown color %s, falling back to white", color_name) params[ATTR_RGB_COLOR] = (255, 255, 255) kelvin = params.pop(ATTR_KELVIN, None) if kelvin is not None: mired = color_util.color_temperature_kelvin_to_mired(kelvin) params[ATTR_COLOR_TEMP] = int(mired) brightness_pct = params.pop(ATTR_BRIGHTNESS_PCT, None) if brightness_pct is not None: params[ATTR_BRIGHTNESS] = int(255 * brightness_pct / 100) xy_color = params.pop(ATTR_XY_COLOR, None) if xy_color is not None: params[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color) rgb_color = params.pop(ATTR_RGB_COLOR, None) if rgb_color is not None: params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color)
def find_hsbk(opp, **kwargs): """Find the desired color from a number of possible inputs.""" hue, saturation, brightness, kelvin = [None] * 4 preprocess_turn_on_alternatives(opp, kwargs) if ATTR_HS_COLOR in kwargs: hue, saturation = kwargs[ATTR_HS_COLOR] elif ATTR_RGB_COLOR in kwargs: hue, saturation = color_util.color_RGB_to_hs(*kwargs[ATTR_RGB_COLOR]) elif ATTR_XY_COLOR in kwargs: hue, saturation = color_util.color_xy_to_hs(*kwargs[ATTR_XY_COLOR]) if hue is not None: hue = int(hue / 360 * 65535) saturation = int(saturation / 100 * 65535) kelvin = 3500 if ATTR_COLOR_TEMP in kwargs: kelvin = int( color_util.color_temperature_mired_to_kelvin( kwargs[ATTR_COLOR_TEMP])) saturation = 0 if ATTR_BRIGHTNESS in kwargs: brightness = convert_8_to_16(kwargs[ATTR_BRIGHTNESS]) hsbk = [hue, saturation, brightness, kelvin] return None if hsbk == [None] * 4 else hsbk
async def async_get_state(self, from_cache=True): """Attempt to retrieve on off state from the light.""" self.debug("polling current state") if self._on_off_channel: self._state = await self._on_off_channel.get_attribute_value( "on_off", from_cache=from_cache) if self._level_channel: self._brightness = await self._level_channel.get_attribute_value( "current_level", from_cache=from_cache) if self._color_channel: color_capabilities = self._color_channel.get_color_capabilities() if (color_capabilities is not None and color_capabilities & CAPABILITIES_COLOR_TEMP): self._color_temp = await self._color_channel.get_attribute_value( "color_temperature", from_cache=from_cache) if (color_capabilities is not None and color_capabilities & CAPABILITIES_COLOR_XY): color_x = await self._color_channel.get_attribute_value( "current_x", from_cache=from_cache) color_y = await self._color_channel.get_attribute_value( "current_y", from_cache=from_cache) if color_x is not None and color_y is not None: self._hs_color = color_util.color_xy_to_hs( float(color_x / 65535), float(color_y / 65535)) if (color_capabilities is not None and color_capabilities & CAPABILITIES_COLOR_LOOP): color_loop_active = await self._color_channel.get_attribute_value( "color_loop_active", from_cache=from_cache) if color_loop_active is not None and color_loop_active == 1: self._effect = light.EFFECT_COLORLOOP
def __post_init__(self) -> None: """Convert xy to hs color.""" if None in (self.color_x, self.color_y): self.hs_color = None return self.hs_color = color_util.color_xy_to_hs(cast(float, self.color_x), cast(float, self.color_y))
def hs_color(self): """Return the hs color value.""" mode = self._color_mode source = self.light.action if self.is_group else self.light.state if mode in ("xy", "hs") and "xy" in source: return color.color_xy_to_hs(*source["xy"], self.gamut) return None
def hs_color(self): """Return the hs color value.""" if self._device.colormode in ("xy", "hs"): if self._device.xy: return color_util.color_xy_to_hs(*self._device.xy) if self._device.hue and self._device.sat: return (self._device.hue / 65535 * 360, self._device.sat / 255 * 100) return None
def __init__(self, unique_id, zha_device: ZhaDeviceType, channels, **kwargs): """Initialize the ZHA light.""" super().__init__(unique_id, zha_device, channels, **kwargs) self._on_off_channel = self.cluster_channels.get(CHANNEL_ON_OFF) self._state = bool(self._on_off_channel.on_off) self._level_channel = self.cluster_channels.get(CHANNEL_LEVEL) self._color_channel = self.cluster_channels.get(CHANNEL_COLOR) self._identify_channel = self.zha_device.channels.identify_ch if self._color_channel: self._min_mireds: int | None = self._color_channel.min_mireds self._max_mireds: int | None = self._color_channel.max_mireds self._cancel_refresh_handle = None effect_list = [] if self._level_channel: self._supported_features |= light.SUPPORT_BRIGHTNESS self._supported_features |= light.SUPPORT_TRANSITION self._brightness = self._level_channel.current_level if self._color_channel: color_capabilities = self._color_channel.color_capabilities if color_capabilities & CAPABILITIES_COLOR_TEMP: self._supported_features |= light.SUPPORT_COLOR_TEMP self._color_temp = self._color_channel.color_temperature if color_capabilities & CAPABILITIES_COLOR_XY: self._supported_features |= light.SUPPORT_COLOR curr_x = self._color_channel.current_x curr_y = self._color_channel.current_y if curr_x is not None and curr_y is not None: self._hs_color = color_util.color_xy_to_hs( float(curr_x / 65535), float(curr_y / 65535) ) else: self._hs_color = (0, 0) if color_capabilities & CAPABILITIES_COLOR_LOOP: self._supported_features |= light.SUPPORT_EFFECT effect_list.append(light.EFFECT_COLORLOOP) if self._color_channel.color_loop_active == 1: self._effect = light.EFFECT_COLORLOOP if self._identify_channel: self._supported_features |= light.SUPPORT_FLASH if effect_list: self._effect_list = effect_list self._default_transition = async_get_zha_config_value( zha_device.gateway.config_entry, ZHA_OPTIONS, CONF_DEFAULT_LIGHT_TRANSITION, 0, )
def hs_color(self): """Define current bulb color.""" if self.wink.supports_xy_color(): return color_util.color_xy_to_hs(*self.wink.color_xy()) if self.wink.supports_hue_saturation(): hue = self.wink.color_hue() saturation = self.wink.color_saturation() if hue is not None and saturation is not None: return hue * 360, saturation * 100 return None
async def async_get_state(self): """Attempt to retrieve the state from the light.""" if not self.available: return self.debug("polling current state") if self._on_off_channel: state = await self._on_off_channel.get_attribute_value( "on_off", from_cache=False ) if state is not None: self._state = state if self._level_channel: level = await self._level_channel.get_attribute_value( "current_level", from_cache=False ) if level is not None: self._brightness = level if self._color_channel: attributes = [ "color_mode", "color_temperature", "current_x", "current_y", "color_loop_active", ] results = await self._color_channel.get_attributes( attributes, from_cache=False ) color_mode = results.get("color_mode") if color_mode is not None: if color_mode == LightColorMode.COLOR_TEMP: color_temp = results.get("color_temperature") if color_temp is not None and color_mode: self._color_temp = color_temp self._hs_color = None else: color_x = results.get("current_x") color_y = results.get("current_y") if color_x is not None and color_y is not None: self._hs_color = color_util.color_xy_to_hs( float(color_x / 65535), float(color_y / 65535) ) self._color_temp = None color_loop_active = results.get("color_loop_active") if color_loop_active is not None: if color_loop_active == 1: self._effect = light.EFFECT_COLORLOOP else: self._effect = None
def test_color_xy_to_hs(): """Test color_xy_to_hs.""" assert color_util.color_xy_to_hs(1, 1) == (47.294, 100) assert color_util.color_xy_to_hs(0.35, 0.35) == (38.182, 12.941) assert color_util.color_xy_to_hs(1, 0) == (345.882, 100) assert color_util.color_xy_to_hs(0, 1) == (120, 100) assert color_util.color_xy_to_hs(0, 0) == (225.176, 100) assert color_util.color_xy_to_hs(1, 0, GAMUT) == (359.294, 100) assert color_util.color_xy_to_hs(0, 1, GAMUT) == (100.706, 100) assert color_util.color_xy_to_hs(0, 0, GAMUT) == (221.463, 96.471)
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(opp, 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) supported_color_modes = light.supported_color_modes # Backwards compatibility: if an RGBWW color is specified, convert to RGB + W # for legacy lights if ATTR_RGBW_COLOR in params: legacy_supported_color_modes = ( light._light_internal_supported_color_modes # pylint: disable=protected-access ) if (COLOR_MODE_RGBW in legacy_supported_color_modes and not supported_color_modes): rgbw_color = params.pop(ATTR_RGBW_COLOR) params[ATTR_RGB_COLOR] = rgbw_color[0:3] params[ATTR_WHITE_VALUE] = rgbw_color[3] # 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 _update(self, force_update=True): """Synchronize state with bridge.""" with self._wemo_exception_handler("update status") as handler: self._update_lights(no_throttle=force_update) self._state = self.wemo.state if handler.success: self._is_on = self._state.get("onoff") != WEMO_OFF self._brightness = self._state.get("level", 255) self._color_temp = self._state.get("temperature_mireds") xy_color = self._state.get("color_xy") if xy_color: self._hs_color = color_util.color_xy_to_hs(*xy_color) else: self._hs_color = None
def _light_internal_convert_color(self, color_mode: str) -> dict: data: dict[str, tuple] = {} if color_mode == COLOR_MODE_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 == COLOR_MODE_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 == COLOR_MODE_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 == COLOR_MODE_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 == COLOR_MODE_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) return data
elif ATTR_RGB_COLOR in params and COLOR_MODE_RGB not in supported_color_modes: rgb_color = params.pop(ATTR_RGB_COLOR) if COLOR_MODE_RGBW in supported_color_modes: params[ATTR_RGBW_COLOR] = color_util.color_rgb_to_rgbw( *rgb_color) elif COLOR_MODE_RGBWW in supported_color_modes: params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( *rgb_color, light.min_mireds, light.max_mireds) elif COLOR_MODE_HS in supported_color_modes: params[ATTR_HS_COLOR] = color_util.color_RGB_to_hs(*rgb_color) elif COLOR_MODE_XY in supported_color_modes: params[ATTR_XY_COLOR] = color_util.color_RGB_to_xy(*rgb_color) elif ATTR_XY_COLOR in params and COLOR_MODE_XY not in supported_color_modes: xy_color = params.pop(ATTR_XY_COLOR) if COLOR_MODE_HS in supported_color_modes: params[ATTR_HS_COLOR] = color_util.color_xy_to_hs(*xy_color) elif COLOR_MODE_RGB in supported_color_modes: params[ATTR_RGB_COLOR] = color_util.color_xy_to_RGB(*xy_color) elif COLOR_MODE_RGBW in supported_color_modes: rgb_color = color_util.color_xy_to_RGB(*xy_color) params[ATTR_RGBW_COLOR] = color_util.color_rgb_to_rgbw( *rgb_color) elif COLOR_MODE_RGBWW in supported_color_modes: rgb_color = color_util.color_xy_to_RGB(*xy_color) params[ATTR_RGBWW_COLOR] = color_util.color_rgb_to_rgbww( *rgb_color, light.min_mireds, light.max_mireds) # Remove deprecated white value if the light supports color mode if supported_color_modes: params.pop(ATTR_WHITE_VALUE, None)
def _update_color(self, values): if not self._config[CONF_COLOR_MODE]: # Deprecated color handling try: red = int(values["color"]["r"]) green = int(values["color"]["g"]) blue = int(values["color"]["b"]) self._hs = color_util.color_RGB_to_hs(red, green, blue) except KeyError: pass except ValueError: _LOGGER.warning("Invalid RGB color value received") return try: x_color = float(values["color"]["x"]) y_color = float(values["color"]["y"]) self._hs = color_util.color_xy_to_hs(x_color, y_color) except KeyError: pass except ValueError: _LOGGER.warning("Invalid XY color value received") return try: hue = float(values["color"]["h"]) saturation = float(values["color"]["s"]) self._hs = (hue, saturation) except KeyError: pass except ValueError: _LOGGER.warning("Invalid HS color value received") return else: color_mode = values["color_mode"] if not self._supports_color_mode(color_mode): _LOGGER.warning("Invalid color mode received") return try: if color_mode == COLOR_MODE_COLOR_TEMP: self._color_temp = int(values["color_temp"]) self._color_mode = COLOR_MODE_COLOR_TEMP elif color_mode == COLOR_MODE_HS: hue = float(values["color"]["h"]) saturation = float(values["color"]["s"]) self._color_mode = COLOR_MODE_HS self._hs = (hue, saturation) elif color_mode == COLOR_MODE_RGB: r = int(values["color"]["r"]) # pylint: disable=invalid-name g = int(values["color"]["g"]) # pylint: disable=invalid-name b = int(values["color"]["b"]) # pylint: disable=invalid-name self._color_mode = COLOR_MODE_RGB self._rgb = (r, g, b) elif color_mode == COLOR_MODE_RGBW: r = int(values["color"]["r"]) # pylint: disable=invalid-name g = int(values["color"]["g"]) # pylint: disable=invalid-name b = int(values["color"]["b"]) # pylint: disable=invalid-name w = int(values["color"]["w"]) # pylint: disable=invalid-name self._color_mode = COLOR_MODE_RGBW self._rgbw = (r, g, b, w) elif color_mode == COLOR_MODE_RGBWW: r = int(values["color"]["r"]) # pylint: disable=invalid-name g = int(values["color"]["g"]) # pylint: disable=invalid-name b = int(values["color"]["b"]) # pylint: disable=invalid-name c = int(values["color"]["c"]) # pylint: disable=invalid-name w = int(values["color"]["w"]) # pylint: disable=invalid-name self._color_mode = COLOR_MODE_RGBWW self._rgbww = (r, g, b, c, w) elif color_mode == COLOR_MODE_XY: x = float(values["color"]["x"]) # pylint: disable=invalid-name y = float(values["color"]["y"]) # pylint: disable=invalid-name self._color_mode = COLOR_MODE_XY self._xy = (x, y) except (KeyError, ValueError): _LOGGER.warning("Invalid or incomplete color value received")
def test_hs_color(): """Test hs_color property.""" light = hue_light.HueLight( light=Mock( state={ "colormode": "ct", "hue": 1234, "sat": 123 }, raw=LIGHT_RAW, colorgamuttype=LIGHT_GAMUT_TYPE, colorgamut=LIGHT_GAMUT, ), coordinator=Mock(last_update_success=True), bridge=Mock(), is_group=False, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, ) assert light.hs_color is None light = hue_light.HueLight( light=Mock( state={ "colormode": "hs", "hue": 1234, "sat": 123 }, raw=LIGHT_RAW, colorgamuttype=LIGHT_GAMUT_TYPE, colorgamut=LIGHT_GAMUT, ), coordinator=Mock(last_update_success=True), bridge=Mock(), is_group=False, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, ) assert light.hs_color is None light = hue_light.HueLight( light=Mock( state={ "colormode": "xy", "hue": 1234, "sat": 123, "xy": [0.4, 0.5] }, raw=LIGHT_RAW, colorgamuttype=LIGHT_GAMUT_TYPE, colorgamut=LIGHT_GAMUT, ), coordinator=Mock(last_update_success=True), bridge=Mock(), is_group=False, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, ) assert light.hs_color == color.color_xy_to_hs(0.4, 0.5, LIGHT_GAMUT)
def state_received(msg): """Handle new MQTT messages.""" values = json.loads(msg.payload) if values["state"] == "ON": self._state = True elif values["state"] == "OFF": self._state = False if self._hs is not None: try: red = int(values["color"]["r"]) green = int(values["color"]["g"]) blue = int(values["color"]["b"]) self._hs = color_util.color_RGB_to_hs(red, green, blue) except KeyError: pass except ValueError: _LOGGER.warning("Invalid RGB color value received") try: x_color = float(values["color"]["x"]) y_color = float(values["color"]["y"]) self._hs = color_util.color_xy_to_hs(x_color, y_color) except KeyError: pass except ValueError: _LOGGER.warning("Invalid XY color value received") try: hue = float(values["color"]["h"]) saturation = float(values["color"]["s"]) self._hs = (hue, saturation) except KeyError: pass except ValueError: _LOGGER.warning("Invalid HS color value received") if self._brightness is not None: try: self._brightness = int( values["brightness"] / float(self._config[CONF_BRIGHTNESS_SCALE]) * 255) except KeyError: pass except ValueError: _LOGGER.warning("Invalid brightness value received") if self._color_temp is not None: try: self._color_temp = int(values["color_temp"]) except KeyError: pass except ValueError: _LOGGER.warning("Invalid color temp value received") if self._effect is not None: try: self._effect = values["effect"] except KeyError: pass except ValueError: _LOGGER.warning("Invalid effect value received") if self._white_value is not None: try: self._white_value = int(values["white_value"]) except KeyError: pass except ValueError: _LOGGER.warning("Invalid white value received") self.async_write_op_state()