parent
5b3711ed19
commit
3efbd6a1c9
|
@ -28,7 +28,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
PLATFORMS: Final = ["light"]
|
PLATFORMS: Final = ["light"]
|
||||||
DISCOVERY_INTERVAL: Final = timedelta(minutes=15)
|
DISCOVERY_INTERVAL: Final = timedelta(minutes=15)
|
||||||
REQUEST_REFRESH_DELAY: Final = 0.65
|
REQUEST_REFRESH_DELAY: Final = 1.5
|
||||||
|
|
||||||
|
|
||||||
async def async_wifi_bulb_for_host(hass: HomeAssistant, host: str) -> WifiLedBulb:
|
async def async_wifi_bulb_for_host(hass: HomeAssistant, host: str) -> WifiLedBulb:
|
||||||
|
|
|
@ -13,7 +13,6 @@ from homeassistant.const import CONF_HOST, CONF_MAC, CONF_MODE, CONF_NAME, CONF_
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||||
|
|
||||||
from . import async_discover_devices, async_wifi_bulb_for_host
|
from . import async_discover_devices, async_wifi_bulb_for_host
|
||||||
|
@ -28,10 +27,6 @@ from .const import (
|
||||||
FLUX_LED_EXCEPTIONS,
|
FLUX_LED_EXCEPTIONS,
|
||||||
FLUX_MAC,
|
FLUX_MAC,
|
||||||
FLUX_MODEL,
|
FLUX_MODEL,
|
||||||
MODE_AUTO,
|
|
||||||
MODE_RGB,
|
|
||||||
MODE_RGBW,
|
|
||||||
MODE_WHITE,
|
|
||||||
TRANSITION_GRADUAL,
|
TRANSITION_GRADUAL,
|
||||||
TRANSITION_JUMP,
|
TRANSITION_JUMP,
|
||||||
TRANSITION_STROBE,
|
TRANSITION_STROBE,
|
||||||
|
@ -233,19 +228,6 @@ class OptionsFlow(config_entries.OptionsFlow):
|
||||||
options = self._config_entry.options
|
options = self._config_entry.options
|
||||||
options_schema = vol.Schema(
|
options_schema = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(
|
|
||||||
CONF_MODE, default=options.get(CONF_MODE, MODE_AUTO)
|
|
||||||
): vol.All(
|
|
||||||
cv.string,
|
|
||||||
vol.In(
|
|
||||||
[
|
|
||||||
MODE_AUTO,
|
|
||||||
MODE_RGBW,
|
|
||||||
MODE_RGB,
|
|
||||||
MODE_WHITE,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_CUSTOM_EFFECT_COLORS,
|
CONF_CUSTOM_EFFECT_COLORS,
|
||||||
default=options.get(CONF_CUSTOM_EFFECT_COLORS, ""),
|
default=options.get(CONF_CUSTOM_EFFECT_COLORS, ""),
|
||||||
|
|
|
@ -28,7 +28,6 @@ MODE_AUTO: Final = "auto"
|
||||||
MODE_RGB: Final = "rgb"
|
MODE_RGB: Final = "rgb"
|
||||||
MODE_RGBW: Final = "rgbw"
|
MODE_RGBW: Final = "rgbw"
|
||||||
|
|
||||||
|
|
||||||
# This mode enables white value to be controlled by brightness.
|
# This mode enables white value to be controlled by brightness.
|
||||||
# RGB value is ignored when this mode is specified.
|
# RGB value is ignored when this mode is specified.
|
||||||
MODE_WHITE: Final = "w"
|
MODE_WHITE: Final = "w"
|
||||||
|
|
|
@ -8,6 +8,21 @@ import random
|
||||||
from typing import Any, Final, cast
|
from typing import Any, Final, cast
|
||||||
|
|
||||||
from flux_led import WifiLedBulb
|
from flux_led import WifiLedBulb
|
||||||
|
from flux_led.const import (
|
||||||
|
COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT,
|
||||||
|
COLOR_MODE_DIM as FLUX_COLOR_MODE_DIM,
|
||||||
|
COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB,
|
||||||
|
COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW,
|
||||||
|
COLOR_MODE_RGBWW as FLUX_COLOR_MODE_RGBWW,
|
||||||
|
)
|
||||||
|
from flux_led.device import MAX_TEMP, MIN_TEMP
|
||||||
|
from flux_led.utils import (
|
||||||
|
color_temp_to_white_levels,
|
||||||
|
rgbcw_brightness,
|
||||||
|
rgbcw_to_rgbwc,
|
||||||
|
rgbw_brightness,
|
||||||
|
rgbww_brightness,
|
||||||
|
)
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
@ -15,16 +30,22 @@ from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
ATTR_EFFECT,
|
ATTR_EFFECT,
|
||||||
ATTR_HS_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
ATTR_WHITE_VALUE,
|
ATTR_RGBW_COLOR,
|
||||||
|
ATTR_RGBWW_COLOR,
|
||||||
|
ATTR_WHITE,
|
||||||
|
COLOR_MODE_BRIGHTNESS,
|
||||||
|
COLOR_MODE_COLOR_TEMP,
|
||||||
|
COLOR_MODE_ONOFF,
|
||||||
|
COLOR_MODE_RGB,
|
||||||
|
COLOR_MODE_RGBW,
|
||||||
|
COLOR_MODE_RGBWW,
|
||||||
|
COLOR_MODE_WHITE,
|
||||||
EFFECT_COLORLOOP,
|
EFFECT_COLORLOOP,
|
||||||
EFFECT_RANDOM,
|
EFFECT_RANDOM,
|
||||||
PLATFORM_SCHEMA,
|
PLATFORM_SCHEMA,
|
||||||
SUPPORT_BRIGHTNESS,
|
|
||||||
SUPPORT_COLOR,
|
|
||||||
SUPPORT_COLOR_TEMP,
|
|
||||||
SUPPORT_EFFECT,
|
SUPPORT_EFFECT,
|
||||||
SUPPORT_WHITE_VALUE,
|
SUPPORT_TRANSITION,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -46,7 +67,10 @@ import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
import homeassistant.util.color as color_util
|
from homeassistant.util.color import (
|
||||||
|
color_temperature_kelvin_to_mired,
|
||||||
|
color_temperature_mired_to_kelvin,
|
||||||
|
)
|
||||||
|
|
||||||
from . import FluxLedUpdateCoordinator
|
from . import FluxLedUpdateCoordinator
|
||||||
from .const import (
|
from .const import (
|
||||||
|
@ -74,7 +98,16 @@ from .const import (
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SUPPORT_FLUX_LED: Final = SUPPORT_BRIGHTNESS | SUPPORT_EFFECT | SUPPORT_COLOR
|
SUPPORT_FLUX_LED: Final = SUPPORT_EFFECT | SUPPORT_TRANSITION
|
||||||
|
|
||||||
|
|
||||||
|
FLUX_COLOR_MODE_TO_HASS: Final = {
|
||||||
|
FLUX_COLOR_MODE_RGB: COLOR_MODE_RGB,
|
||||||
|
FLUX_COLOR_MODE_RGBW: COLOR_MODE_RGBW,
|
||||||
|
FLUX_COLOR_MODE_RGBWW: COLOR_MODE_RGBWW,
|
||||||
|
FLUX_COLOR_MODE_CCT: COLOR_MODE_COLOR_TEMP,
|
||||||
|
FLUX_COLOR_MODE_DIM: COLOR_MODE_WHITE,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Constant color temp values for 2 flux_led special modes
|
# Constant color temp values for 2 flux_led special modes
|
||||||
|
@ -128,8 +161,6 @@ EFFECT_MAP: Final = {
|
||||||
EFFECT_ID_NAME: Final = {v: k for k, v in EFFECT_MAP.items()}
|
EFFECT_ID_NAME: Final = {v: k for k, v in EFFECT_MAP.items()}
|
||||||
EFFECT_CUSTOM_CODE: Final = 0x60
|
EFFECT_CUSTOM_CODE: Final = 0x60
|
||||||
|
|
||||||
WHITE_MODES: Final = {MODE_RGBW}
|
|
||||||
|
|
||||||
FLUX_EFFECT_LIST: Final = sorted(EFFECT_MAP) + [EFFECT_RANDOM]
|
FLUX_EFFECT_LIST: Final = sorted(EFFECT_MAP) + [EFFECT_RANDOM]
|
||||||
|
|
||||||
SERVICE_CUSTOM_EFFECT: Final = "set_custom_effect"
|
SERVICE_CUSTOM_EFFECT: Final = "set_custom_effect"
|
||||||
|
@ -243,7 +274,6 @@ async def async_setup_entry(
|
||||||
coordinator,
|
coordinator,
|
||||||
entry.unique_id,
|
entry.unique_id,
|
||||||
entry.data[CONF_NAME],
|
entry.data[CONF_NAME],
|
||||||
options.get(CONF_MODE) or MODE_AUTO,
|
|
||||||
list(custom_effect_colors),
|
list(custom_effect_colors),
|
||||||
options.get(CONF_CUSTOM_EFFECT_SPEED_PCT, DEFAULT_EFFECT_SPEED),
|
options.get(CONF_CUSTOM_EFFECT_SPEED_PCT, DEFAULT_EFFECT_SPEED),
|
||||||
options.get(CONF_CUSTOM_EFFECT_TRANSITION, TRANSITION_GRADUAL),
|
options.get(CONF_CUSTOM_EFFECT_TRANSITION, TRANSITION_GRADUAL),
|
||||||
|
@ -262,7 +292,6 @@ class FluxLight(CoordinatorEntity, LightEntity):
|
||||||
coordinator: FluxLedUpdateCoordinator,
|
coordinator: FluxLedUpdateCoordinator,
|
||||||
unique_id: str | None,
|
unique_id: str | None,
|
||||||
name: str,
|
name: str,
|
||||||
mode: str,
|
|
||||||
custom_effect_colors: list[tuple[int, int, int]],
|
custom_effect_colors: list[tuple[int, int, int]],
|
||||||
custom_effect_speed_pct: int,
|
custom_effect_speed_pct: int,
|
||||||
custom_effect_transition: str,
|
custom_effect_transition: str,
|
||||||
|
@ -272,18 +301,29 @@ class FluxLight(CoordinatorEntity, LightEntity):
|
||||||
self._bulb: WifiLedBulb = coordinator.device
|
self._bulb: WifiLedBulb = coordinator.device
|
||||||
self._attr_name = name
|
self._attr_name = name
|
||||||
self._attr_unique_id = unique_id
|
self._attr_unique_id = unique_id
|
||||||
self._ip_address = coordinator.host
|
self._attr_supported_features = SUPPORT_FLUX_LED
|
||||||
self._mode = mode
|
self._attr_min_mireds = (
|
||||||
|
color_temperature_kelvin_to_mired(MAX_TEMP) + 1
|
||||||
|
) # for rounding
|
||||||
|
self._attr_max_mireds = color_temperature_kelvin_to_mired(MIN_TEMP)
|
||||||
|
self._attr_supported_color_modes = {
|
||||||
|
FLUX_COLOR_MODE_TO_HASS.get(mode, COLOR_MODE_ONOFF)
|
||||||
|
for mode in self._bulb.color_modes
|
||||||
|
}
|
||||||
|
self._attr_effect_list = FLUX_EFFECT_LIST
|
||||||
|
if custom_effect_colors:
|
||||||
|
self._attr_effect_list = [*FLUX_EFFECT_LIST, EFFECT_CUSTOM]
|
||||||
self._custom_effect_colors = custom_effect_colors
|
self._custom_effect_colors = custom_effect_colors
|
||||||
self._custom_effect_speed_pct = custom_effect_speed_pct
|
self._custom_effect_speed_pct = custom_effect_speed_pct
|
||||||
self._custom_effect_transition = custom_effect_transition
|
self._custom_effect_transition = custom_effect_transition
|
||||||
old_protocol = self._bulb.protocol == "LEDENET_ORIGINAL"
|
|
||||||
if self.unique_id:
|
if self.unique_id:
|
||||||
|
old_protocol = self._bulb.protocol == "LEDENET_ORIGINAL"
|
||||||
|
raw_state = self._bulb.raw_state
|
||||||
self._attr_device_info = {
|
self._attr_device_info = {
|
||||||
"connections": {(dr.CONNECTION_NETWORK_MAC, self.unique_id)},
|
"connections": {(dr.CONNECTION_NETWORK_MAC, self.unique_id)},
|
||||||
ATTR_MODEL: f"0x{self._bulb.raw_state[1]:02X}",
|
ATTR_MODEL: f"0x{self._bulb.model_num:02X}",
|
||||||
ATTR_SW_VERSION: "1" if old_protocol else str(self._bulb.raw_state[10]),
|
|
||||||
ATTR_NAME: self.name,
|
ATTR_NAME: self.name,
|
||||||
|
ATTR_SW_VERSION: "1" if old_protocol else str(raw_state.version_number),
|
||||||
ATTR_MANUFACTURER: "FluxLED/Magic Home",
|
ATTR_MANUFACTURER: "FluxLED/Magic Home",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,49 +335,53 @@ class FluxLight(CoordinatorEntity, LightEntity):
|
||||||
@property
|
@property
|
||||||
def brightness(self) -> int:
|
def brightness(self) -> int:
|
||||||
"""Return the brightness of this light between 0..255."""
|
"""Return the brightness of this light between 0..255."""
|
||||||
if self._mode == MODE_WHITE:
|
|
||||||
return self.white_value
|
|
||||||
return cast(int, self._bulb.brightness)
|
return cast(int, self._bulb.brightness)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hs_color(self) -> tuple[float, float] | None:
|
def color_temp(self) -> int:
|
||||||
"""Return the color property."""
|
"""Return the kelvin value of this light in mired."""
|
||||||
return color_util.color_RGB_to_hs(*self._bulb.getRgb())
|
return color_temperature_kelvin_to_mired(self._bulb.getWhiteTemperature()[0])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def rgb_color(self) -> tuple[int, int, int]:
|
||||||
"""Flag supported features."""
|
"""Return the rgb color value."""
|
||||||
if self._mode == MODE_WHITE:
|
rgb: tuple[int, int, int] = self._bulb.rgb
|
||||||
return SUPPORT_BRIGHTNESS
|
return rgb
|
||||||
if self._mode in WHITE_MODES:
|
|
||||||
return SUPPORT_FLUX_LED | SUPPORT_WHITE_VALUE | SUPPORT_COLOR_TEMP
|
|
||||||
return SUPPORT_FLUX_LED
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def white_value(self) -> int:
|
def rgbw_color(self) -> tuple[int, int, int, int]:
|
||||||
"""Return the white value of this light between 0..255."""
|
"""Return the rgbw color value."""
|
||||||
return cast(int, self._bulb.getRgbw()[3])
|
rgbw: tuple[int, int, int, int] = self._bulb.rgbw
|
||||||
|
return rgbw
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def effect_list(self) -> list[str]:
|
def rgbww_color(self) -> tuple[int, int, int, int, int]:
|
||||||
"""Return the list of supported effects."""
|
"""Return the rgbww aka rgbcw color value."""
|
||||||
if self._custom_effect_colors:
|
rgbcw: tuple[int, int, int, int, int] = self._bulb.rgbcw
|
||||||
return FLUX_EFFECT_LIST + [EFFECT_CUSTOM]
|
return rgbcw
|
||||||
return FLUX_EFFECT_LIST
|
|
||||||
|
@property
|
||||||
|
def rgbwc_color(self) -> tuple[int, int, int, int, int]:
|
||||||
|
"""Return the rgbwc color value."""
|
||||||
|
rgbwc: tuple[int, int, int, int, int] = self._bulb.rgbww
|
||||||
|
return rgbwc
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color_mode(self) -> str:
|
||||||
|
"""Return the color mode of the light."""
|
||||||
|
return FLUX_COLOR_MODE_TO_HASS.get(self._bulb.color_mode, COLOR_MODE_ONOFF)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def effect(self) -> str | None:
|
def effect(self) -> str | None:
|
||||||
"""Return the current effect."""
|
"""Return the current effect."""
|
||||||
if (current_mode := self._bulb.raw_state[3]) == EFFECT_CUSTOM_CODE:
|
if (current_mode := self._bulb.raw_state.preset_pattern) == EFFECT_CUSTOM_CODE:
|
||||||
return EFFECT_CUSTOM
|
return EFFECT_CUSTOM
|
||||||
return EFFECT_ID_NAME.get(current_mode)
|
return EFFECT_ID_NAME.get(current_mode)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> dict[str, str]:
|
def extra_state_attributes(self) -> dict[str, str]:
|
||||||
"""Return the attributes."""
|
"""Return the attributes."""
|
||||||
return {
|
return {"ip_address": self._bulb.ipaddr}
|
||||||
"ip_address": self._ip_address,
|
|
||||||
}
|
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the specified or all lights on."""
|
"""Turn the specified or all lights on."""
|
||||||
|
@ -349,39 +393,63 @@ class FluxLight(CoordinatorEntity, LightEntity):
|
||||||
"""Turn the specified or all lights on."""
|
"""Turn the specified or all lights on."""
|
||||||
if not self.is_on:
|
if not self.is_on:
|
||||||
self._bulb.turnOn()
|
self._bulb.turnOn()
|
||||||
|
if not kwargs:
|
||||||
if hs_color := kwargs.get(ATTR_HS_COLOR):
|
|
||||||
rgb: tuple[int, int, int] | None = color_util.color_hs_to_RGB(*hs_color)
|
|
||||||
else:
|
|
||||||
rgb = None
|
|
||||||
|
|
||||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
|
||||||
# handle special modes
|
|
||||||
if (color_temp := kwargs.get(ATTR_COLOR_TEMP)) is not None:
|
|
||||||
if brightness is None:
|
|
||||||
brightness = self.brightness
|
|
||||||
if color_temp > COLOR_TEMP_WARM_VS_COLD_WHITE_CUT_OFF:
|
|
||||||
self._bulb.setRgbw(w=brightness)
|
|
||||||
else:
|
|
||||||
self._bulb.setRgbw(w2=brightness)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
white = kwargs.get(ATTR_WHITE_VALUE)
|
if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is None:
|
||||||
effect = kwargs.get(ATTR_EFFECT)
|
brightness = self.brightness
|
||||||
# Show warning if effect set with rgb, brightness, or white level
|
|
||||||
if effect and (brightness or white or rgb):
|
|
||||||
_LOGGER.warning(
|
|
||||||
"RGB, brightness and white level are ignored when"
|
|
||||||
" an effect is specified for a flux bulb"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# Handle switch to CCT Color Mode
|
||||||
|
if ATTR_COLOR_TEMP in kwargs:
|
||||||
|
color_temp_mired = kwargs[ATTR_COLOR_TEMP]
|
||||||
|
color_temp_kelvin = color_temperature_mired_to_kelvin(color_temp_mired)
|
||||||
|
if self.color_mode != COLOR_MODE_RGBWW:
|
||||||
|
self._bulb.setWhiteTemperature(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._bulb.getWhiteTemperature()[1]
|
||||||
|
)
|
||||||
|
cold, warm = color_temp_to_white_levels(color_temp_kelvin, brightness)
|
||||||
|
self._bulb.set_levels(r=0, b=0, g=0, w=warm, w2=cold)
|
||||||
|
return
|
||||||
|
# Handle switch to HS Color Mode
|
||||||
|
if ATTR_RGB_COLOR in kwargs:
|
||||||
|
self._bulb.set_levels(*kwargs[ATTR_RGB_COLOR], brightness=brightness)
|
||||||
|
return
|
||||||
|
# Handle switch to RGBW Color Mode
|
||||||
|
if ATTR_RGBW_COLOR in kwargs:
|
||||||
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
|
rgbw = rgbw_brightness(kwargs[ATTR_RGBW_COLOR], brightness)
|
||||||
|
else:
|
||||||
|
rgbw = kwargs[ATTR_RGBW_COLOR]
|
||||||
|
self._bulb.set_levels(*rgbw)
|
||||||
|
return
|
||||||
|
# Handle switch to RGBWW Color Mode
|
||||||
|
if ATTR_RGBWW_COLOR in kwargs:
|
||||||
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
|
rgbcw = rgbcw_brightness(kwargs[ATTR_RGBWW_COLOR], brightness)
|
||||||
|
else:
|
||||||
|
rgbcw = kwargs[ATTR_RGBWW_COLOR]
|
||||||
|
self._bulb.set_levels(*rgbcw_to_rgbwc(rgbcw))
|
||||||
|
return
|
||||||
|
# Handle switch to White Color Mode
|
||||||
|
if ATTR_WHITE in kwargs:
|
||||||
|
self._bulb.set_levels(w=kwargs[ATTR_WHITE])
|
||||||
|
return
|
||||||
|
if ATTR_EFFECT in kwargs:
|
||||||
|
effect = kwargs[ATTR_EFFECT]
|
||||||
# Random color effect
|
# Random color effect
|
||||||
if effect == EFFECT_RANDOM:
|
if effect == EFFECT_RANDOM:
|
||||||
self._bulb.setRgb(
|
self._bulb.set_levels(
|
||||||
random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
|
random.randint(0, 255),
|
||||||
|
random.randint(0, 255),
|
||||||
|
random.randint(0, 255),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Custom effect
|
# Custom effect
|
||||||
if effect == EFFECT_CUSTOM:
|
if effect == EFFECT_CUSTOM:
|
||||||
if self._custom_effect_colors:
|
if self._custom_effect_colors:
|
||||||
|
@ -391,35 +459,35 @@ class FluxLight(CoordinatorEntity, LightEntity):
|
||||||
self._custom_effect_transition,
|
self._custom_effect_transition,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Effect selection
|
# Effect selection
|
||||||
if effect in EFFECT_MAP:
|
if effect in EFFECT_MAP:
|
||||||
self._bulb.setPresetPattern(EFFECT_MAP[effect], 50)
|
self._bulb.setPresetPattern(EFFECT_MAP[effect], DEFAULT_EFFECT_SPEED)
|
||||||
return
|
return
|
||||||
|
raise ValueError(f"Unknown effect {effect}")
|
||||||
# Preserve current brightness on color/white level change
|
# Handle brightness adjustment in CCT Color Mode
|
||||||
if brightness is None:
|
if self.color_mode == COLOR_MODE_COLOR_TEMP:
|
||||||
brightness = self.brightness
|
self._bulb.setWhiteTemperature(
|
||||||
|
self._bulb.getWhiteTemperature()[0], brightness
|
||||||
# handle W only mode (use brightness instead of white value)
|
)
|
||||||
if self._mode == MODE_WHITE:
|
|
||||||
self._bulb.setRgbw(0, 0, 0, w=brightness)
|
|
||||||
return
|
return
|
||||||
|
# Handle brightness adjustment in RGB Color Mode
|
||||||
if white is None and self._mode in WHITE_MODES:
|
if self.color_mode == COLOR_MODE_RGB:
|
||||||
white = self.white_value
|
self._bulb.set_levels(*self.rgb_color, brightness=brightness)
|
||||||
|
|
||||||
# Preserve color on brightness/white level change
|
|
||||||
if rgb is None:
|
|
||||||
rgb = self._bulb.getRgb()
|
|
||||||
|
|
||||||
# handle RGBW mode
|
|
||||||
if self._mode == MODE_RGBW:
|
|
||||||
self._bulb.setRgbw(*tuple(rgb), w=white, brightness=brightness)
|
|
||||||
return
|
return
|
||||||
|
# Handle brightness adjustment in RGBW Color Mode
|
||||||
# handle RGB mode
|
if self.color_mode == COLOR_MODE_RGBW:
|
||||||
self._bulb.setRgb(*tuple(rgb), brightness=brightness)
|
self._bulb.set_levels(*rgbw_brightness(self.rgbw_color, brightness))
|
||||||
|
return
|
||||||
|
# Handle brightness adjustment in RGBWW Color Mode
|
||||||
|
if self.color_mode == COLOR_MODE_RGBWW:
|
||||||
|
rgbwc = self.rgbwc_color
|
||||||
|
self._bulb.set_levels(*rgbww_brightness(rgbwc, brightness))
|
||||||
|
return
|
||||||
|
# Handle White Color Mode and Brightness Only Color Mode
|
||||||
|
if self.color_mode in (COLOR_MODE_WHITE, COLOR_MODE_BRIGHTNESS):
|
||||||
|
self._bulb.set_levels(w=brightness)
|
||||||
|
return
|
||||||
|
raise ValueError(f"Unsupported color mode {self.color_mode}")
|
||||||
|
|
||||||
def set_custom_effect(
|
def set_custom_effect(
|
||||||
self, colors: list[tuple[int, int, int]], speed_pct: int, transition: str
|
self, colors: list[tuple[int, int, int]], speed_pct: int, transition: str
|
||||||
|
@ -436,24 +504,3 @@ class FluxLight(CoordinatorEntity, LightEntity):
|
||||||
await self.hass.async_add_executor_job(self._bulb.turnOff)
|
await self.hass.async_add_executor_job(self._bulb.turnOff)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
|
||||||
"""When entity is added to hass."""
|
|
||||||
await super().async_added_to_hass()
|
|
||||||
if self._mode and self._mode != MODE_AUTO:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._bulb.mode == "ww":
|
|
||||||
self._mode = MODE_WHITE
|
|
||||||
elif self._bulb.rgbwcapable:
|
|
||||||
self._mode = MODE_RGBW
|
|
||||||
else:
|
|
||||||
self._mode = MODE_RGB
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Detected mode for %s (%s) with raw_state=%s rgbwcapable=%s is %s",
|
|
||||||
self.name,
|
|
||||||
self.unique_id,
|
|
||||||
self._bulb.raw_state,
|
|
||||||
self._bulb.rgbwcapable,
|
|
||||||
self._mode,
|
|
||||||
)
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Flux LED/MagicHome",
|
"name": "Flux LED/MagicHome",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/flux_led",
|
"documentation": "https://www.home-assistant.io/integrations/flux_led",
|
||||||
"requirements": ["flux_led==0.22"],
|
"requirements": ["flux_led==0.24.3"],
|
||||||
"codeowners": ["@icemanch"],
|
"codeowners": ["@icemanch"],
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"dhcp": [
|
"dhcp": [
|
||||||
|
@ -30,4 +30,3 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -649,7 +649,7 @@ fjaraskupan==1.0.1
|
||||||
flipr-api==1.4.1
|
flipr-api==1.4.1
|
||||||
|
|
||||||
# homeassistant.components.flux_led
|
# homeassistant.components.flux_led
|
||||||
flux_led==0.22
|
flux_led==0.24.3
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
fnvhash==0.1.0
|
fnvhash==0.1.0
|
||||||
|
|
|
@ -378,7 +378,7 @@ fjaraskupan==1.0.1
|
||||||
flipr-api==1.4.1
|
flipr-api==1.4.1
|
||||||
|
|
||||||
# homeassistant.components.flux_led
|
# homeassistant.components.flux_led
|
||||||
flux_led==0.22
|
flux_led==0.24.3
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
fnvhash==0.1.0
|
fnvhash==0.1.0
|
||||||
|
|
|
@ -5,6 +5,11 @@ import socket
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from flux_led import WifiLedBulb
|
from flux_led import WifiLedBulb
|
||||||
|
from flux_led.const import (
|
||||||
|
COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT,
|
||||||
|
COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB,
|
||||||
|
)
|
||||||
|
from flux_led.protocol import LEDENETRawState
|
||||||
|
|
||||||
from homeassistant.components.dhcp import (
|
from homeassistant.components.dhcp import (
|
||||||
HOSTNAME as DHCP_HOSTNAME,
|
HOSTNAME as DHCP_HOSTNAME,
|
||||||
|
@ -34,9 +39,21 @@ def _mocked_bulb() -> WifiLedBulb:
|
||||||
bulb = MagicMock(auto_spec=WifiLedBulb)
|
bulb = MagicMock(auto_spec=WifiLedBulb)
|
||||||
bulb.getRgb = MagicMock(return_value=[255, 0, 0])
|
bulb.getRgb = MagicMock(return_value=[255, 0, 0])
|
||||||
bulb.getRgbw = MagicMock(return_value=[255, 0, 0, 50])
|
bulb.getRgbw = MagicMock(return_value=[255, 0, 0, 50])
|
||||||
|
bulb.getRgbww = MagicMock(return_value=[255, 0, 0, 50, 0])
|
||||||
|
bulb.getRgbcw = MagicMock(return_value=[255, 0, 0, 0, 50])
|
||||||
|
bulb.rgb = (255, 0, 0)
|
||||||
|
bulb.rgbw = (255, 0, 0, 50)
|
||||||
|
bulb.rgbww = (255, 0, 0, 50, 0)
|
||||||
|
bulb.rgbcw = (255, 0, 0, 0, 50)
|
||||||
|
bulb.getWhiteTemperature = MagicMock(return_value=(2700, 128))
|
||||||
bulb.brightness = 128
|
bulb.brightness = 128
|
||||||
|
bulb.model_num = 0x35
|
||||||
bulb.rgbwcapable = True
|
bulb.rgbwcapable = True
|
||||||
bulb.raw_state = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
bulb.color_modes = {FLUX_COLOR_MODE_RGB, FLUX_COLOR_MODE_CCT}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGB
|
||||||
|
bulb.raw_state = LEDENETRawState(
|
||||||
|
0, 0x35, 0, 0x61, 0x5, 50, 255, 0, 0, 50, 8, 0, 0, 0
|
||||||
|
)
|
||||||
return bulb
|
return bulb
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ from homeassistant.components.flux_led.const import (
|
||||||
CONF_CUSTOM_EFFECT_SPEED_PCT,
|
CONF_CUSTOM_EFFECT_SPEED_PCT,
|
||||||
CONF_CUSTOM_EFFECT_TRANSITION,
|
CONF_CUSTOM_EFFECT_TRANSITION,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
MODE_AUTO,
|
|
||||||
MODE_RGB,
|
MODE_RGB,
|
||||||
TRANSITION_JUMP,
|
TRANSITION_JUMP,
|
||||||
TRANSITION_STROBE,
|
TRANSITION_STROBE,
|
||||||
|
@ -436,7 +435,6 @@ async def test_options(hass: HomeAssistant):
|
||||||
assert result["step_id"] == "init"
|
assert result["step_id"] == "init"
|
||||||
|
|
||||||
user_input = {
|
user_input = {
|
||||||
CONF_MODE: MODE_AUTO,
|
|
||||||
CONF_CUSTOM_EFFECT_COLORS: "[0,0,255], [255,0,0]",
|
CONF_CUSTOM_EFFECT_COLORS: "[0,0,255], [255,0,0]",
|
||||||
CONF_CUSTOM_EFFECT_SPEED_PCT: 50,
|
CONF_CUSTOM_EFFECT_SPEED_PCT: 50,
|
||||||
CONF_CUSTOM_EFFECT_TRANSITION: TRANSITION_JUMP,
|
CONF_CUSTOM_EFFECT_TRANSITION: TRANSITION_JUMP,
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
"""Tests for light platform."""
|
"""Tests for light platform."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
from flux_led.const import (
|
||||||
|
COLOR_MODE_ADDRESSABLE as FLUX_COLOR_MODE_ADDRESSABLE,
|
||||||
|
COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT,
|
||||||
|
COLOR_MODE_DIM as FLUX_COLOR_MODE_DIM,
|
||||||
|
COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB,
|
||||||
|
COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW,
|
||||||
|
COLOR_MODE_RGBWW as FLUX_COLOR_MODE_RGBWW,
|
||||||
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import flux_led
|
from homeassistant.components import flux_led
|
||||||
|
@ -25,7 +34,11 @@ from homeassistant.components.light import (
|
||||||
ATTR_EFFECT,
|
ATTR_EFFECT,
|
||||||
ATTR_EFFECT_LIST,
|
ATTR_EFFECT_LIST,
|
||||||
ATTR_HS_COLOR,
|
ATTR_HS_COLOR,
|
||||||
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_RGBW_COLOR,
|
||||||
|
ATTR_RGBWW_COLOR,
|
||||||
ATTR_SUPPORTED_COLOR_MODES,
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
|
ATTR_WHITE,
|
||||||
DOMAIN as LIGHT_DOMAIN,
|
DOMAIN as LIGHT_DOMAIN,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -108,8 +121,9 @@ async def test_light_device_registry(
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
bulb.protocol = protocol
|
bulb.protocol = protocol
|
||||||
bulb.raw_state[1] = model
|
bulb.raw_state = bulb.raw_state._replace(model_num=model, version_number=sw_version)
|
||||||
bulb.raw_state[10] = sw_version
|
bulb.model_num = model
|
||||||
|
|
||||||
with _patch_discovery(no_device=True), _patch_wifibulb(device=bulb):
|
with _patch_discovery(no_device=True), _patch_wifibulb(device=bulb):
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -131,8 +145,9 @@ async def test_rgb_light(hass: HomeAssistant) -> None:
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
bulb.rgbwcapable = False
|
bulb.raw_state = bulb.raw_state._replace(model_num=0x33) # RGB only model
|
||||||
bulb.protocol = None
|
bulb.color_modes = {FLUX_COLOR_MODE_RGB}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGB
|
||||||
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -143,9 +158,9 @@ async def test_rgb_light(hass: HomeAssistant) -> None:
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 128
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
assert attributes[ATTR_COLOR_MODE] == "hs"
|
assert attributes[ATTR_COLOR_MODE] == "rgb"
|
||||||
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["hs"]
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgb"]
|
||||||
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
|
@ -170,8 +185,8 @@ async def test_rgb_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgb.assert_called_with(255, 0, 0, brightness=100)
|
bulb.set_levels.assert_called_with(255, 0, 0, brightness=100)
|
||||||
bulb.setRgb.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -179,8 +194,8 @@ async def test_rgb_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_HS_COLOR: (10, 30)},
|
{ATTR_ENTITY_ID: entity_id, ATTR_HS_COLOR: (10, 30)},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgb.assert_called_with(255, 191, 178, brightness=128)
|
bulb.set_levels.assert_called_with(255, 191, 178, brightness=128)
|
||||||
bulb.setRgb.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -188,8 +203,8 @@ async def test_rgb_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"},
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgb.assert_called_once()
|
bulb.set_levels.assert_called_once()
|
||||||
bulb.setRgb.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -200,6 +215,137 @@ async def test_rgb_light(hass: HomeAssistant) -> None:
|
||||||
bulb.setPresetPattern.assert_called_with(43, 50)
|
bulb.setPresetPattern.assert_called_with(43, 50)
|
||||||
bulb.setPresetPattern.reset_mock()
|
bulb.setPresetPattern.reset_mock()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "does not exist"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_rgb_cct_light(hass: HomeAssistant) -> None:
|
||||||
|
"""Test an rgb cct light."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE},
|
||||||
|
unique_id=MAC_ADDRESS,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
bulb = _mocked_bulb()
|
||||||
|
bulb.raw_state = bulb.raw_state._replace(model_num=0x35) # RGB & CCT model
|
||||||
|
bulb.color_modes = {FLUX_COLOR_MODE_RGB, FLUX_COLOR_MODE_CCT}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGB
|
||||||
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity_id = "light.az120444_aabbccddeeff"
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
attributes = state.attributes
|
||||||
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
|
assert attributes[ATTR_COLOR_MODE] == "rgb"
|
||||||
|
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
||||||
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "rgb"]
|
||||||
|
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
)
|
||||||
|
bulb.turnOff.assert_called_once()
|
||||||
|
|
||||||
|
bulb.is_on = False
|
||||||
|
async_fire_time_changed(hass, utcnow() + timedelta(seconds=10))
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert hass.states.get(entity_id).state == STATE_OFF
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
)
|
||||||
|
bulb.turnOn.assert_called_once()
|
||||||
|
bulb.turnOn.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.set_levels.assert_called_with(255, 0, 0, brightness=100)
|
||||||
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_HS_COLOR: (10, 30)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.set_levels.assert_called_with(255, 191, 178, brightness=128)
|
||||||
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.set_levels.assert_called_once()
|
||||||
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "purple_fade"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.setPresetPattern.assert_called_with(43, 50)
|
||||||
|
bulb.setPresetPattern.reset_mock()
|
||||||
|
|
||||||
|
bulb.is_on = True
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_CCT
|
||||||
|
bulb.getWhiteTemperature = Mock(return_value=(5000, 128))
|
||||||
|
bulb.raw_state = bulb.raw_state._replace(
|
||||||
|
red=0, green=0, blue=0, warm_white=1, cool_white=2
|
||||||
|
)
|
||||||
|
async_fire_time_changed(hass, utcnow() + timedelta(seconds=60))
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
attributes = state.attributes
|
||||||
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
|
assert attributes[ATTR_COLOR_MODE] == "color_temp"
|
||||||
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "rgb"]
|
||||||
|
assert attributes[ATTR_COLOR_TEMP] == 200
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 370},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.setWhiteTemperature.assert_called_with(2702, 128)
|
||||||
|
bulb.setWhiteTemperature.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 255},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.setWhiteTemperature.assert_called_with(5000, 255)
|
||||||
|
bulb.setWhiteTemperature.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.setWhiteTemperature.assert_called_with(5000, 128)
|
||||||
|
bulb.setWhiteTemperature.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
async def test_rgbw_light(hass: HomeAssistant) -> None:
|
async def test_rgbw_light(hass: HomeAssistant) -> None:
|
||||||
"""Test an rgbw light."""
|
"""Test an rgbw light."""
|
||||||
|
@ -210,6 +356,8 @@ async def test_rgbw_light(hass: HomeAssistant) -> None:
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
|
bulb.color_modes = {FLUX_COLOR_MODE_RGBW}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGBW
|
||||||
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -222,8 +370,8 @@ async def test_rgbw_light(hass: HomeAssistant) -> None:
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 128
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
assert attributes[ATTR_COLOR_MODE] == "rgbw"
|
assert attributes[ATTR_COLOR_MODE] == "rgbw"
|
||||||
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs", "rgbw"]
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgbw"]
|
||||||
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
assert attributes[ATTR_RGB_COLOR] == (255, 42, 42)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
@ -240,6 +388,7 @@ async def test_rgbw_light(hass: HomeAssistant) -> None:
|
||||||
)
|
)
|
||||||
bulb.turnOn.assert_called_once()
|
bulb.turnOn.assert_called_once()
|
||||||
bulb.turnOn.reset_mock()
|
bulb.turnOn.reset_mock()
|
||||||
|
bulb.is_on = True
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -247,35 +396,41 @@ async def test_rgbw_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(255, 0, 0, w=50, brightness=100)
|
bulb.set_levels.assert_called_with(168, 0, 0, 33)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
"turn_on",
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 150},
|
{
|
||||||
|
ATTR_ENTITY_ID: entity_id,
|
||||||
|
ATTR_RGBW_COLOR: (255, 255, 255, 255),
|
||||||
|
ATTR_BRIGHTNESS: 128,
|
||||||
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(w2=128)
|
bulb.set_levels.assert_called_with(128, 128, 128, 128)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
"turn_on",
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 290},
|
{ATTR_ENTITY_ID: entity_id, ATTR_RGBW_COLOR: (255, 255, 255, 255)},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(w=128)
|
bulb.set_levels.assert_called_with(255, 255, 255, 255)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
"turn_on",
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_HS_COLOR: (10, 30)},
|
{ATTR_ENTITY_ID: entity_id, ATTR_RGBW_COLOR: (255, 191, 178, 0)},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(255, 191, 178, w=50, brightness=128)
|
bulb.set_levels.assert_called_with(255, 191, 178, 0)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -283,8 +438,8 @@ async def test_rgbw_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"},
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgb.assert_called_once()
|
bulb.set_levels.assert_called_once()
|
||||||
bulb.setRgb.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -305,9 +460,9 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None:
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
bulb.raw_state[9] = 1
|
bulb.raw_state = bulb.raw_state._replace(warm_white=1, cool_white=2)
|
||||||
bulb.raw_state[11] = 2
|
bulb.color_modes = {FLUX_COLOR_MODE_RGBWW, FLUX_COLOR_MODE_CCT}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGBWW
|
||||||
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -318,10 +473,10 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None:
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 128
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
assert attributes[ATTR_COLOR_MODE] == "rgbw"
|
assert attributes[ATTR_COLOR_MODE] == "rgbww"
|
||||||
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs", "rgbw"]
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "rgbww"]
|
||||||
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
assert attributes[ATTR_HS_COLOR] == (3.237, 94.51)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
@ -345,17 +500,49 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(255, 0, 0, w=50, brightness=100)
|
bulb.set_levels.assert_called_with(250, 0, 0, 49, 0)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
bulb.is_on = True
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
"turn_on",
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 150},
|
{
|
||||||
|
ATTR_ENTITY_ID: entity_id,
|
||||||
|
ATTR_RGBWW_COLOR: (255, 255, 255, 0, 255),
|
||||||
|
ATTR_BRIGHTNESS: 128,
|
||||||
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(w2=128)
|
bulb.set_levels.assert_called_with(192, 192, 192, 192, 0)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_RGBWW_COLOR: (255, 255, 255, 255, 50)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.set_levels.assert_called_with(255, 255, 255, 50, 255)
|
||||||
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.set_levels.assert_called_with(r=0, b=0, g=0, w=0, w2=127)
|
||||||
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154, ATTR_BRIGHTNESS: 255},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.set_levels.assert_called_with(r=0, b=0, g=0, w=0, w2=255)
|
||||||
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -363,17 +550,17 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 290},
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 290},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(w=128)
|
bulb.set_levels.assert_called_with(r=0, b=0, g=0, w=102, w2=25)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
"turn_on",
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_HS_COLOR: (10, 30)},
|
{ATTR_ENTITY_ID: entity_id, ATTR_RGBWW_COLOR: (255, 191, 178, 0, 0)},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(255, 191, 178, w=50, brightness=128)
|
bulb.set_levels.assert_called_with(255, 191, 178, 0, 0)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -381,8 +568,8 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"},
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgb.assert_called_once()
|
bulb.set_levels.assert_called_once()
|
||||||
bulb.setRgb.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -405,6 +592,8 @@ async def test_white_light(hass: HomeAssistant) -> None:
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
bulb.mode = "ww"
|
bulb.mode = "ww"
|
||||||
bulb.protocol = None
|
bulb.protocol = None
|
||||||
|
bulb.color_modes = {FLUX_COLOR_MODE_DIM}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_DIM
|
||||||
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -414,9 +603,9 @@ async def test_white_light(hass: HomeAssistant) -> None:
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 50
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
assert attributes[ATTR_COLOR_MODE] == "brightness"
|
assert attributes[ATTR_COLOR_MODE] == "white"
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["brightness"]
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["white"]
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
@ -440,13 +629,20 @@ async def test_white_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.setRgbw.assert_called_with(0, 0, 0, w=100)
|
bulb.set_levels.assert_called_with(w=100)
|
||||||
bulb.setRgbw.reset_mock()
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_WHITE: 100},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.set_levels.assert_called_with(w=100)
|
||||||
|
bulb.set_levels.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
async def test_rgb_light_custom_effects(
|
async def test_rgb_light_custom_effects(hass: HomeAssistant) -> None:
|
||||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|
||||||
) -> None:
|
|
||||||
"""Test an rgb light with a custom effect."""
|
"""Test an rgb light with a custom effect."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
|
@ -461,6 +657,8 @@ async def test_rgb_light_custom_effects(
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
|
bulb.color_modes = {FLUX_COLOR_MODE_RGB}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGB
|
||||||
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -471,9 +669,9 @@ async def test_rgb_light_custom_effects(
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 128
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
assert attributes[ATTR_COLOR_MODE] == "rgbw"
|
assert attributes[ATTR_COLOR_MODE] == "rgb"
|
||||||
assert attributes[ATTR_EFFECT_LIST] == [*FLUX_EFFECT_LIST, "custom"]
|
assert attributes[ATTR_EFFECT_LIST] == [*FLUX_EFFECT_LIST, "custom"]
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs", "rgbw"]
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgb"]
|
||||||
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
|
@ -494,7 +692,7 @@ async def test_rgb_light_custom_effects(
|
||||||
)
|
)
|
||||||
bulb.setCustomPattern.assert_called_with([[0, 0, 255], [255, 0, 0]], 88, "jump")
|
bulb.setCustomPattern.assert_called_with([[0, 0, 255], [255, 0, 0]], 88, "jump")
|
||||||
bulb.setCustomPattern.reset_mock()
|
bulb.setCustomPattern.reset_mock()
|
||||||
bulb.raw_state[3] = EFFECT_CUSTOM_CODE
|
bulb.raw_state = bulb.raw_state._replace(preset_pattern=EFFECT_CUSTOM_CODE)
|
||||||
bulb.is_on = True
|
bulb.is_on = True
|
||||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=20))
|
async_fire_time_changed(hass, utcnow() + timedelta(seconds=20))
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -503,7 +701,6 @@ async def test_rgb_light_custom_effects(
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_EFFECT] == "custom"
|
assert attributes[ATTR_EFFECT] == "custom"
|
||||||
|
|
||||||
caplog.clear()
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
"turn_on",
|
||||||
|
@ -512,7 +709,7 @@ async def test_rgb_light_custom_effects(
|
||||||
)
|
)
|
||||||
bulb.setCustomPattern.assert_called_with([[0, 0, 255], [255, 0, 0]], 88, "jump")
|
bulb.setCustomPattern.assert_called_with([[0, 0, 255], [255, 0, 0]], 88, "jump")
|
||||||
bulb.setCustomPattern.reset_mock()
|
bulb.setCustomPattern.reset_mock()
|
||||||
bulb.raw_state[3] = EFFECT_CUSTOM_CODE
|
bulb.raw_state = bulb.raw_state._replace(preset_pattern=EFFECT_CUSTOM_CODE)
|
||||||
bulb.is_on = True
|
bulb.is_on = True
|
||||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=20))
|
async_fire_time_changed(hass, utcnow() + timedelta(seconds=20))
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -520,7 +717,6 @@ async def test_rgb_light_custom_effects(
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_EFFECT] == "custom"
|
assert attributes[ATTR_EFFECT] == "custom"
|
||||||
assert "RGB, brightness and white level are ignored when" in caplog.text
|
|
||||||
|
|
||||||
|
|
||||||
async def test_rgb_light_custom_effects_invalid_colors(hass: HomeAssistant) -> None:
|
async def test_rgb_light_custom_effects_invalid_colors(hass: HomeAssistant) -> None:
|
||||||
|
@ -538,6 +734,8 @@ async def test_rgb_light_custom_effects_invalid_colors(hass: HomeAssistant) -> N
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
|
bulb.color_modes = {FLUX_COLOR_MODE_RGB}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGB
|
||||||
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -548,9 +746,9 @@ async def test_rgb_light_custom_effects_invalid_colors(hass: HomeAssistant) -> N
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 128
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
assert attributes[ATTR_COLOR_MODE] == "rgbw"
|
assert attributes[ATTR_COLOR_MODE] == "rgb"
|
||||||
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs", "rgbw"]
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgb"]
|
||||||
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
||||||
|
|
||||||
|
|
||||||
|
@ -565,6 +763,8 @@ async def test_rgb_light_custom_effect_via_service(
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
|
bulb.color_modes = {FLUX_COLOR_MODE_RGB}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGB
|
||||||
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -575,9 +775,9 @@ async def test_rgb_light_custom_effect_via_service(
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
attributes = state.attributes
|
attributes = state.attributes
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 128
|
assert attributes[ATTR_BRIGHTNESS] == 128
|
||||||
assert attributes[ATTR_COLOR_MODE] == "rgbw"
|
assert attributes[ATTR_COLOR_MODE] == "rgb"
|
||||||
assert attributes[ATTR_EFFECT_LIST] == [*FLUX_EFFECT_LIST]
|
assert attributes[ATTR_EFFECT_LIST] == [*FLUX_EFFECT_LIST]
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs", "rgbw"]
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["rgb"]
|
||||||
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
|
@ -605,34 +805,6 @@ async def test_rgb_light_custom_effect_via_service(
|
||||||
bulb.setCustomPattern.reset_mock()
|
bulb.setCustomPattern.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
async def test_rgbw_detection_without_protocol(hass: HomeAssistant) -> None:
|
|
||||||
"""Test an rgbw detection without protocol."""
|
|
||||||
config_entry = MockConfigEntry(
|
|
||||||
domain=DOMAIN,
|
|
||||||
data={CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE},
|
|
||||||
unique_id=MAC_ADDRESS,
|
|
||||||
)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
bulb = _mocked_bulb()
|
|
||||||
bulb.protocol = None
|
|
||||||
bulb.rgbwprotocol = None
|
|
||||||
bulb.rgbwcapable = True
|
|
||||||
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
|
||||||
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
entity_id = "light.az120444_aabbccddeeff"
|
|
||||||
|
|
||||||
state = hass.states.get(entity_id)
|
|
||||||
assert state.state == STATE_ON
|
|
||||||
attributes = state.attributes
|
|
||||||
assert attributes[ATTR_BRIGHTNESS] == 128
|
|
||||||
assert attributes[ATTR_COLOR_MODE] == "rgbw"
|
|
||||||
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
|
||||||
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs", "rgbw"]
|
|
||||||
assert attributes[ATTR_HS_COLOR] == (0, 100)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_migrate_from_yaml(hass: HomeAssistant) -> None:
|
async def test_migrate_from_yaml(hass: HomeAssistant) -> None:
|
||||||
"""Test migrate from yaml."""
|
"""Test migrate from yaml."""
|
||||||
config = {
|
config = {
|
||||||
|
@ -680,3 +852,54 @@ async def test_migrate_from_yaml(hass: HomeAssistant) -> None:
|
||||||
CONF_CUSTOM_EFFECT_SPEED_PCT: 30,
|
CONF_CUSTOM_EFFECT_SPEED_PCT: 30,
|
||||||
CONF_CUSTOM_EFFECT_TRANSITION: "strobe",
|
CONF_CUSTOM_EFFECT_TRANSITION: "strobe",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_addressable_light(hass: HomeAssistant) -> None:
|
||||||
|
"""Test an addressable light."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE},
|
||||||
|
unique_id=MAC_ADDRESS,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
bulb = _mocked_bulb()
|
||||||
|
bulb.raw_state = bulb.raw_state._replace(model_num=0x33) # RGB only model
|
||||||
|
bulb.color_modes = {FLUX_COLOR_MODE_ADDRESSABLE}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_ADDRESSABLE
|
||||||
|
with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb):
|
||||||
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity_id = "light.az120444_aabbccddeeff"
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
attributes = state.attributes
|
||||||
|
assert attributes[ATTR_COLOR_MODE] == "onoff"
|
||||||
|
assert attributes[ATTR_EFFECT_LIST] == FLUX_EFFECT_LIST
|
||||||
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["onoff"]
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
)
|
||||||
|
bulb.turnOff.assert_called_once()
|
||||||
|
|
||||||
|
bulb.is_on = False
|
||||||
|
async_fire_time_changed(hass, utcnow() + timedelta(seconds=10))
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert hass.states.get(entity_id).state == STATE_OFF
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
)
|
||||||
|
bulb.turnOn.assert_called_once()
|
||||||
|
bulb.turnOn.reset_mock()
|
||||||
|
bulb.is_on = True
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue