text: """Support for Magic Home lights."""
  from __future__ import annotations
  
  import ast
  import logging
  from typing import Any, Final
  
  from flux_led.const import MultiColorEffects
  from flux_led.protocol import MusicMode
  from flux_led.utils import (
      color_temp_to_white_levels,
      rgbcw_brightness,
      rgbcw_to_rgbwc,
      rgbw_brightness,
  )
  import voluptuous as vol
  
  from homeassistant import config_entries
  from homeassistant.components.light import (
      ATTR_BRIGHTNESS,
      ATTR_COLOR_TEMP,
      ATTR_EFFECT,
      ATTR_RGB_COLOR,
      ATTR_RGBW_COLOR,
      ATTR_RGBWW_COLOR,
      ATTR_WHITE,
      COLOR_MODE_RGBWW,
      SUPPORT_EFFECT,
      SUPPORT_TRANSITION,
      LightEntity,
  )
  from homeassistant.const import CONF_NAME
  from homeassistant.core import HomeAssistant, callback
  from homeassistant.helpers import entity_platform
  import homeassistant.helpers.config_validation as cv
  from homeassistant.helpers.entity_platform import AddEntitiesCallback
  from homeassistant.helpers.update_coordinator import CoordinatorEntity
  from homeassistant.util.color import (
      color_temperature_kelvin_to_mired,
      color_temperature_mired_to_kelvin,
  )
  
  from .const import (
      CONF_COLORS,
      CONF_CUSTOM_EFFECT_COLORS,
      CONF_CUSTOM_EFFECT_SPEED_PCT,
      CONF_CUSTOM_EFFECT_TRANSITION,
      CONF_EFFECT,
      CONF_SPEED_PCT,
      CONF_TRANSITION,
      DEFAULT_EFFECT_SPEED,
      DOMAIN,
      TRANSITION_GRADUAL,
      TRANSITION_JUMP,
      TRANSITION_STROBE,
  )
  from .coordinator import FluxLedUpdateCoordinator
  from .entity import FluxOnOffEntity
  from .util import (
      _effect_brightness,
      _flux_color_mode_to_hass,
      _hass_color_modes,
      _str_to_multi_color_effect,
  )
  
  _LOGGER = logging.getLogger(__name__)
  
  MODE_ATTRS = {
      ATTR_EFFECT,
      ATTR_COLOR_TEMP,
      ATTR_RGB_COLOR,
      ATTR_RGBW_COLOR,
      ATTR_RGBWW_COLOR,
      ATTR_WHITE,
  }
  
  ATTR_FOREGROUND_COLOR: Final = "foreground_color"
  ATTR_BACKGROUND_COLOR: Final = "background_color"
  ATTR_SENSITIVITY: Final = "sensitivity"
  ATTR_LIGHT_SCREEN: Final = "light_screen"
  
  # Constant color temp values for 2 flux_led special modes
  # Warm-white and Cool-white modes
  COLOR_TEMP_WARM_VS_COLD_WHITE_CUT_OFF: Final = 285
  
  EFFECT_CUSTOM: Final = "custom"
  
  SERVICE_CUSTOM_EFFECT: Final = "set_custom_effect"
  SERVICE_SET_ZONES: Final = "set_zones"
  SERVICE_SET_MUSIC_MODE: Final = "set_music_mode"
  
  CUSTOM_EFFECT_DICT: Final = {
      vol.Required(CONF_COLORS): vol.All(
          cv.ensure_list,
          vol.Length(min=1, max=16),
          [vol.All(vol.Coerce(tuple), vol.ExactSequence((cv.byte, cv.byte, cv.byte)))],
      ),
      vol.Optional(CONF_SPEED_PCT, default=50): vol.All(
          vol.Coerce(int), vol.Range(min=0, max=100)
      ),
      vol.Optional(CONF_TRANSITION, default=TRANSITION_GRADUAL): vol.All(
          cv.string, vol.In([TRANSITION_GRADUAL, TRANSITION_JUMP, TRANSITION_STROBE])
      ),
  }
  
  SET_MUSIC_MODE_DICT: Final = {
      vol.Optional(ATTR_SENSITIVITY, default=100): vol.All(
          vol.Coerce(int), vol.Range(min=0, max=100)
      ),
      vol.Optional(ATTR_BRIGHTNESS, default=100): vol.All(
          vol.Coerce(int), vol.Range(min=0, max=100)
      ),
      vol.Optional(ATTR_EFFECT, default=1): vol.All(
          vol.Coerce(int), vol.Range(min=1, max=16)
      ),
      vol.Optional(ATTR_LIGHT_SCREEN, default=False): bool,
      vol.Optional(ATTR_FOREGROUND_COLOR): vol.All(
          vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 3)
      ),
      vol.Optional(ATTR_BACKGROUND_COLOR): vol.All(
          vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 3)
      ),
  }
  
  SET_ZONES_DICT: Final = {
      vol.Required(CONF_COLORS): vol.All(
          cv.ensure_list,
          vol.Length(min=1, max=2048),
          [vol.All(vol.Coerce(tuple), vol.ExactSequence((cv.byte, cv.byte, cv.byte)))],
      ),
      vol.Optional(CONF_SPEED_PCT, default=50): vol.All(
          vol.Coerce(int), vol.Range(min=0, max=100)
      ),
      vol.Optional(CONF_EFFECT, default=MultiColorEffects.STATIC.name.lower()): vol.All(
          cv.string, vol.In([effect.name.lower() for effect in MultiColorEffects])
      ),
  }
  
  
  async def async_setup_entry(
      hass: HomeAssistant,
      entry: config_entries.ConfigEntry,
      async_add_entities: AddEntitiesCallback,
  ) -> None:
      """Set up the Flux lights."""
      coordinator: FluxLedUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
  
      platform = entity_platform.async_get_current_platform()
      platform.async_register_entity_service(
          SERVICE_CUSTOM_EFFECT,
          CUSTOM_EFFECT_DICT,
          "async_set_custom_effect",
      )
      platform.async_register_entity_service(
          SERVICE_SET_ZONES,
          SET_ZONES_DICT,
          "async_set_zones",
      )
      platform.async_register_entity_service(
          SERVICE_SET_MUSIC_MODE,
          SET_MUSIC_MODE_DICT,
          "async_set_music_mode",
      )
      options = entry.options
  
      try:
          custom_effect_colors = ast.literal_eval(
              options.get(CONF_CUSTOM_EFFECT_COLORS) or "[]"
          )
      except (ValueError, TypeError, SyntaxError, MemoryError) as ex:
          _LOGGER.warning(
              "Could not parse custom effect colors for %s: %s", entry.unique_id, ex
          )
          custom_effect_colors = []
  
      async_add_entities(
          [
              FluxLight(
                  coordinator,
                  entry.unique_id,
                  entry.data[CONF_NAME],
                  list(custom_effect_colors),
                  options.get(CONF_CUSTOM_EFFECT_SPEED_PCT, DEFAULT_EFFECT_SPEED),
                  options.get(CONF_CUSTOM_EFFECT_TRANSITION, TRANSITION_GRADUAL),
              )
          ]
      )
  
  
  class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity):
      """Representation of a Flux light."""
  
      _attr_supported_features = SUPPORT_TRANSITION | SUPPORT_EFFECT
  
      def __init__(
          self,
          coordinator: FluxLedUpdateCoordinator,
          unique_id: str | None,
          name: str,
          custom_effect_colors: list[tuple[int, int, int]],
          custom_effect_speed_pct: int,
          custom_effect_transition: str,
      ) -> None:
          """Initialize the light."""
          super().__init__(coordinator, unique_id, name, None)
          self._attr_min_mireds = (
              color_temperature_kelvin_to_mired(self._device.max_temp) + 1
          )  # for rounding
          self._attr_max_mireds = color_temperature_kelvin_to_mired(self._device.min_temp)
          self._attr_supported_color_modes = _hass_color_modes(self._device)
          custom_effects: list[str] = []
          if custom_effect_colors:
              custom_effects.append(EFFECT_CUSTOM)
          self._attr_effect_list = [*self._device.effect_list, *custom_effects]
          self._custom_effect_colors = custom_effect_colors
          self._custom_effect_speed_pct = custom_effect_speed_pct
          self._custom_effect_transition = custom_effect_transition
  
      @property
      def brightness(self) -> int:
          """Return the brightness of this light between 0..255."""
          return self._device.brightness
  
      @property
      def color_temp(self) -> int:
          """Return the kelvin value of this light in mired."""
          return color_temperature_kelvin_to_mired(self._device.color_temp)
  
      @property
      def rgb_color(self) -> tuple[int, int, int]:
          """Return the rgb color value."""
          return self._device.rgb_unscaled
  
      @property
      def rgbw_color(self) -> tuple[int, int, int, int]:
          """Return the rgbw color value."""
          return self._device.rgbw
  
      @property
      def rgbww_color(self) -> tuple[int, int, int, int, int]:
          """Return the rgbww aka rgbcw color value."""
          return self._device.rgbcw
  
      @property
      def color_mode(self) -> str:
          """Return the color mode of the light."""
          return _flux_color_mode_to_hass(
              self._device.color_mode, self._device.color_modes
          )
  
      @property
      def effect(self) -> str | None:
          """Return the current effect."""
          return self._device.effect
  
      async def _async_turn_on(self, **kwargs: Any) -> None:
          """Turn the specified or all lights on."""
          if self._device.requires_turn_on or not kwargs:
              if not self.is_on:
                  await self._device.async_turn_on()
              if not kwargs:
                  return
  
          if MODE_ATTRS.intersection(kwargs):
              await self._async_set_mode(**kwargs)
              return
          await self._device.async_set_brightness(self._async_brightness(**kwargs))
  
      async def _async_set_effect(self, effect: str, brightness: int) -> None:
          """Set an effect."""
          # Custom effect
          if effect == EFFECT_CUSTOM:
              if self._custom_effect_colors:
                  await self._device.async_set_custom_pattern(
                      self._custom_effect_colors,
                      self._custom_effect_speed_pct,
                      self._custom_effect_transition,
                  )
              return
          await self._device.async_set_effect(
              effect,
              self._device.speed or DEFAULT_EFFECT_SPEED,
              _effect_brightness(brightness),
          )
  
      @callback
      def _async_brightness(self, **kwargs: Any) -> int:
          """Determine brightness from kwargs or current value."""
          if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is None:
              brightness = self.brightness
          if not brightness:
              # If the brightness was previously 0, the light
              # will not turn on unless brightness is at least 1
              # If the device was on and brightness was not
              # set, it means it was masked by an effect
              brightness = 255 if self.is_on else 1
          return brightness
  
      async def _async_set_mode(self, **kwargs: Any) -> None:
          """Set an effect or color mode."""
          brightness = self._async_brightness(**kwargs)
          # Handle switch to Effect Mode
          if effect := kwargs.get(ATTR_EFFECT):
              await self._async_set_effect(effect, brightness)
              return
          # Handle switch to CCT Color Mode
          if color_temp_mired := kwargs.get(ATTR_COLOR_TEMP):
              color_temp_kelvin = color_temperature_mired_to_kelvin(color_temp_mired)
              if self.color_mode != COLOR_MODE_RGBWW:
                  await self._device.async_set_white_temp(color_temp_kelvin, brightness)
                  return
  
              # When switching to color temp from RGBWW mode,
              # we do not want the overall brightness, we only
              # want the brightness of the white channels
              brightness = kwargs.get(
                  ATTR_BRIGHTNESS, self._device.getWhiteTemperature()[1]
              )
              channels = color_temp_to_white_levels(color_temp_kelvin, brightness)
              warm = channels.warm_white
              cold = channels.cool_white
              await self._device.async_set_levels(r=0, b=0, g=0, w=warm, w2=cold)
              return
          # Handle switch to RGB Color Mode
          if rgb := kwargs.get(ATTR_RGB_COLOR):
              red, green, blue = rgb
              await self._device.async_set_levels(red, green, blue, brightness=brightness)
              return
          # Handle switch to RGBW Color Mode
          if rgbw := kwargs.get(ATTR_RGBW_COLOR):
              if ATTR_BRIGHTNESS in kwargs:
                  rgbw = rgbw_brightness(rgbw, brightness)
              await self._device.async_set_levels(*rgbw)
              return
          # Handle switch to RGBWW Color Mode
          if rgbcw := kwargs.get(ATTR_RGBWW_COLOR):
              if ATTR_BRIGHTNESS in kwargs:
                  rgbcw = rgbcw_brightness(kwargs[ATTR_RGBWW_COLOR], brightness)
              await self._device.async_set_levels(*rgbcw_to_rgbwc(rgbcw))
              return
          if (white := kwargs.get(ATTR_WHITE)) is not None:
              await self._device.async_set_levels(w=white)
              return
  
      async def async_set_custom_effect(
          self, colors: list[tuple[int, int, int]], speed_pct: int, transition: str
      ) -> None:
          """Set a custom effect on the bulb."""
          await self._device.async_set_custom_pattern(
              colors,
              speed_pct,
              transition,
          )
  
      async def async_set_zones(
          self, colors: list[tuple[int, int, int]], speed_pct: int, effect: str
      ) -> None:
          """Set a colors for zones."""
          await self._device.async_set_zones(
              colors,
              speed_pct,
              _str_to_multi_color_effect(effect),
          )
  
      async def async_set_music_mode(
          self,
          sensitivity: int,
          brightness: int,
          effect: int,
          light_screen: bool,
          foreground_color: tuple[int, int, int] | None = None,
          background_color: tuple[int, int, int] | None = None,
      ) -> None:
          """Configure music mode."""
          await self._async_ensure_device_on()
          await self._device.async_set_music_mode(
              sensitivity=sensitivity,
              brightness=brightness,
              mode=MusicMode.LIGHT_SCREEN.value if light_screen else None,
              effect=effect,
              foreground_color=foreground_color,
              background_color=background_color,
          )
  