# Source code for kasa.interfaces.light

    """Interact with a TPLink Light.

    >>> from kasa import Discover, Module
    >>>
    >>> dev = await Discover.discover_single(
    >>>     "127.0.0.3",
    >>>     username="user@example.com",
    >>>     password="great_password"
    >>> )
    >>> await dev.update()
    >>> print(dev.alias)
    Living Room Bulb

    Lights, like any other supported devices, can be turned on and off:

    >>> print(dev.is_on)
    >>> await dev.turn_on()
    >>> await dev.update()
    >>> print(dev.is_on)
    True

    Get the light module to interact:

    >>> light = dev.modules[Module.Light]

    You can use the ``has_feature()`` method to check for supported features:

    >>> light.has_feature("brightness")
    True
    >>> light.has_feature("hsv")
    True
    >>> light.has_feature("color_temp")
    True

    All known bulbs support changing the brightness:

    >>> light.brightness
    100
    >>> await light.set_brightness(50)
    >>> await dev.update()
    >>> light.brightness
    50

    Bulbs supporting color temperature can be queried for the supported range:

    >>> if color_temp_feature := light.get_feature("color_temp"):
    >>>     print(f"{color_temp_feature.minimum_value}, {color_temp_feature.maximum_value}")
    2500, 6500
    >>> await light.set_color_temp(3000)
    >>> await dev.update()
    >>> light.color_temp
    3000

    Color bulbs can be adjusted by passing hue, saturation and value:

    >>> await light.set_hsv(180, 100, 80)
    >>> await dev.update()
    >>> light.hsv
    HSV(hue=180, saturation=100, value=80)

    """

    from __future__ import annotations

    from abc import ABC, abstractmethod
    from dataclasses import dataclass
    from typing import TYPE_CHECKING, Annotated, Any, NamedTuple
    from warnings import warn

    from ..exceptions import KasaException
    from ..module import FeatureAttribute, Module

    @dataclass
    class LightState:
        """Class for smart light preset info."""

        light_on: bool | None = None
        brightness: int | None = None
        hue: int | None = None
        saturation: int | None = None
        color_temp: int | None = None
        transition: int | None = None

    class ColorTempRange(NamedTuple):
        """Color temperature range."""

        min: int
        max: int

    class HSV(NamedTuple):
        """Hue-saturation-value."""

        hue: int
        saturation: int
        value: int

    class Light(Module, ABC):
        """Base class for TP-Link Light."""

        @property
        @abstractmethod
        def hsv(self) -> Annotated[HSV, FeatureAttribute()]:
            """Return the current HSV state of the bulb.

            :return: hue, saturation and value (degrees, %, %)
            """

        @property
        @abstractmethod
        def color_temp(self) -> Annotated[int, FeatureAttribute()]:
            """Whether the bulb supports color temperature changes."""

        @property
        @abstractmethod
        def brightness(self) -> Annotated[int, FeatureAttribute()]:
            """Return the current brightness in percentage."""

        @abstractmethod
        async def set_hsv(
            self,
            hue: int,
            saturation: int,
            value: int | None = None,
            *,
            transition: int | None = None,
        ) -> Annotated[dict, FeatureAttribute()]:
            """Set new HSV.

            Note, transition is not supported and will be ignored.

            :param int hue: hue in degrees
            :param int saturation: saturation in percentage [0,100]
            :param int value: value in percentage [0, 100]
            :param int transition: transition in milliseconds.
            """

        @abstractmethod
        async def set_color_temp(
            self, temp: int, *, brightness: int | None = None, transition: int | None = None
        ) -> Annotated[dict, FeatureAttribute()]:
            """Set the color temperature of the device in kelvin.

            Note, transition is not supported and will be ignored.

            :param int temp: The new color temperature, in Kelvin
            :param int transition: transition in milliseconds.
            """

        @abstractmethod
        async def set_brightness(
            self, brightness: int, *, transition: int | None = None
        ) -> Annotated[dict, FeatureAttribute()]:
            """Set the brightness in percentage.

            Note, transition is not supported and will be ignored.

            :param int brightness: brightness in percent
            :param int transition: transition in milliseconds.
            """

        @property
        @abstractmethod
        def state(self) -> LightState:
            """Return the current light state."""

        @abstractmethod
        async def set_state(self, state: LightState) -> dict:
            """Set the light state."""

        def _deprecated_valid_temperature_range(self) -> ColorTempRange:
            if not (temp := self.get_feature("color_temp")):
                raise KasaException("Color temperature not supported")
            return ColorTempRange(temp.minimum_value, temp.maximum_value)

        def _deprecated_attributes(self, dep_name: str) -> str | None:
            map: dict[str, str] = {
                "is_color": "hsv",
                "is_dimmable": "brightness",
                "is_variable_color_temp": "color_temp",
            }
            return map.get(dep_name)

        if not TYPE_CHECKING:

            def __getattr__(self, name: str) -> Any:
                if name == "valid_temperature_range":
                    msg = (
                        "valid_temperature_range is deprecated, use "
                        'get_feature("color_temp") minimum_value '
                        " and maximum_value instead"
                    )
                    warn(msg, DeprecationWarning, stacklevel=2)
                    res = self._deprecated_valid_temperature_range()
                    return res

                if name == "has_effects":
                    msg = (
                        "has_effects is deprecated, check `Module.LightEffect "
                        "in device.modules` instead"
                    )
                    warn(msg, DeprecationWarning, stacklevel=2)
                    return Module.LightEffect in self._device.modules

                if attr := self._deprecated_attributes(name):
                    msg = f'{name} is deprecated, use has_feature("{attr}") instead'
                    warn(msg, DeprecationWarning, stacklevel=2)
                    return self.has_feature(attr)

                raise AttributeError(f"Energy module has no attribute {name!r}")