diff --git a/homeassistant/components/flux_led/__init__.py b/homeassistant/components/flux_led/__init__.py index ddd7a0bdbe8..efab893be42 100644 --- a/homeassistant/components/flux_led/__init__.py +++ b/homeassistant/components/flux_led/__init__.py @@ -7,7 +7,7 @@ from typing import Any, Final, cast from flux_led import DeviceType from flux_led.aio import AIOWifiLedBulb -from flux_led.const import ATTR_ID +from flux_led.const import ATTR_ID, WhiteChannelType from flux_led.scanner import FluxLEDDiscovery from homeassistant.config_entries import ConfigEntry @@ -23,6 +23,7 @@ from homeassistant.helpers.event import ( from homeassistant.helpers.typing import ConfigType from .const import ( + CONF_WHITE_CHANNEL_TYPE, DISCOVER_SCAN_TIMEOUT, DOMAIN, FLUX_LED_DISCOVERY, @@ -56,6 +57,9 @@ PLATFORMS_BY_TYPE: Final = { } DISCOVERY_INTERVAL: Final = timedelta(minutes=15) REQUEST_REFRESH_DELAY: Final = 1.5 +NAME_TO_WHITE_CHANNEL_TYPE: Final = { + option.name.lower(): option for option in WhiteChannelType +} @callback @@ -95,6 +99,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: device: AIOWifiLedBulb = async_wifi_bulb_for_host(host, discovery=discovery) signal = SIGNAL_STATE_UPDATED.format(device.ipaddr) device.discovery = discovery + if white_channel_type := entry.data.get(CONF_WHITE_CHANNEL_TYPE): + device.white_channel_channel_type = NAME_TO_WHITE_CHANNEL_TYPE[ + white_channel_type + ] @callback def _async_state_changed(*_: Any) -> None: diff --git a/homeassistant/components/flux_led/const.py b/homeassistant/components/flux_led/const.py index ec81f7ba087..69663555d36 100644 --- a/homeassistant/components/flux_led/const.py +++ b/homeassistant/components/flux_led/const.py @@ -28,6 +28,7 @@ FLUX_COLOR_MODE_TO_HASS: Final = { FLUX_COLOR_MODE_CCT: COLOR_MODE_COLOR_TEMP, } +MULTI_BRIGHTNESS_COLOR_MODES: Final = {COLOR_MODE_RGBWW, COLOR_MODE_RGBW} API: Final = "flux_api" @@ -57,6 +58,8 @@ CONF_MINOR_VERSION: Final = "minor_version" CONF_REMOTE_ACCESS_ENABLED: Final = "remote_access_enabled" CONF_REMOTE_ACCESS_HOST: Final = "remote_access_host" CONF_REMOTE_ACCESS_PORT: Final = "remote_access_port" +CONF_WHITE_CHANNEL_TYPE: Final = "white_channel_type" + TRANSITION_GRADUAL: Final = "gradual" TRANSITION_JUMP: Final = "jump" diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index 998ca513e6c..e63b8250de8 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -7,12 +7,7 @@ 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, -) +from flux_led.utils import rgbcw_brightness, rgbcw_to_rgbwc, rgbw_brightness import voluptuous as vol from homeassistant import config_entries @@ -24,7 +19,6 @@ from homeassistant.components.light import ( ATTR_RGBW_COLOR, ATTR_RGBWW_COLOR, ATTR_WHITE, - COLOR_MODE_RGBWW, SUPPORT_EFFECT, SUPPORT_TRANSITION, LightEntity, @@ -50,6 +44,7 @@ from .const import ( CONF_TRANSITION, DEFAULT_EFFECT_SPEED, DOMAIN, + MULTI_BRIGHTNESS_COLOR_MODES, TRANSITION_GRADUAL, TRANSITION_JUMP, TRANSITION_STROBE, @@ -203,9 +198,7 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): ) -> 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_min_mireds = color_temperature_kelvin_to_mired(self._device.max_temp) 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] = [] @@ -306,20 +299,14 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): # 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) + if ( + ATTR_BRIGHTNESS not in kwargs + and self.color_mode in MULTI_BRIGHTNESS_COLOR_MODES + ): + # When switching to color temp from RGBWW or RGB&W mode, + # we do not want the overall brightness of the RGB channels + brightness = max(self._device.rgb) + await self._device.async_set_white_temp(color_temp_kelvin, brightness) return # Handle switch to RGB Color Mode if rgb := kwargs.get(ATTR_RGB_COLOR): diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index f949ad455ac..670e9f9328a 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["network"], "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.27.45"], + "requirements": ["flux_led==0.28.1"], "quality_scale": "platinum", "codeowners": ["@icemanch", "@bdraco"], "iot_class": "local_push", diff --git a/homeassistant/components/flux_led/select.py b/homeassistant/components/flux_led/select.py index be99148a72a..1f86f79d511 100644 --- a/homeassistant/components/flux_led/select.py +++ b/homeassistant/components/flux_led/select.py @@ -3,6 +3,7 @@ from __future__ import annotations from flux_led.aio import AIOWifiLedBulb from flux_led.base_device import DeviceType +from flux_led.const import DEFAULT_WHITE_CHANNEL_TYPE, WhiteChannelType from flux_led.protocol import PowerRestoreState, RemoteConfig from homeassistant import config_entries @@ -12,9 +13,14 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN +from .const import CONF_WHITE_CHANNEL_TYPE, DOMAIN, FLUX_COLOR_MODE_RGBW from .coordinator import FluxLedUpdateCoordinator from .entity import FluxBaseEntity, FluxEntity +from .util import _human_readable_option + +NAME_TO_POWER_RESTORE_STATE = { + _human_readable_option(option.name): option for option in PowerRestoreState +} async def async_setup_entry( @@ -31,6 +37,7 @@ async def async_setup_entry( | FluxWiringsSelect | FluxICTypeSelect | FluxRemoteConfigSelect + | FluxWhiteChannelSelect ] = [] name = entry.data[CONF_NAME] unique_id = entry.unique_id @@ -57,20 +64,30 @@ async def async_setup_entry( coordinator, unique_id, f"{name} Remote Config", "remote_config" ) ) + if FLUX_COLOR_MODE_RGBW in device.color_modes: + entities.append(FluxWhiteChannelSelect(coordinator.device, entry)) if entities: async_add_entities(entities) -def _human_readable_option(const_option: str) -> str: - return const_option.replace("_", " ").title() +class FluxConfigAtStartSelect(FluxBaseEntity, SelectEntity): + """Representation of a flux config entity that only updates at start or change.""" + + _attr_entity_category = EntityCategory.CONFIG -class FluxPowerStateSelect(FluxBaseEntity, SelectEntity): +class FluxConfigSelect(FluxEntity, SelectEntity): + """Representation of a flux config entity that updates.""" + + _attr_entity_category = EntityCategory.CONFIG + + +class FluxPowerStateSelect(FluxConfigAtStartSelect, SelectEntity): """Representation of a Flux power restore state option.""" _attr_icon = "mdi:transmission-tower-off" - _attr_entity_category = EntityCategory.CONFIG + _attr_options = list(NAME_TO_POWER_RESTORE_STATE) def __init__( self, @@ -82,10 +99,6 @@ class FluxPowerStateSelect(FluxBaseEntity, SelectEntity): self._attr_name = f"{entry.data[CONF_NAME]} Power Restored" if entry.unique_id: self._attr_unique_id = f"{entry.unique_id}_power_restored" - self._name_to_state = { - _human_readable_option(option.name): option for option in PowerRestoreState - } - self._attr_options = list(self._name_to_state) self._async_set_current_option_from_device() @callback @@ -98,17 +111,13 @@ class FluxPowerStateSelect(FluxBaseEntity, SelectEntity): async def async_select_option(self, option: str) -> None: """Change the power state.""" - await self._device.async_set_power_restore(channel1=self._name_to_state[option]) + await self._device.async_set_power_restore( + channel1=NAME_TO_POWER_RESTORE_STATE[option] + ) self._async_set_current_option_from_device() self.async_write_ha_state() -class FluxConfigSelect(FluxEntity, SelectEntity): - """Representation of a flux config entity that updates.""" - - _attr_entity_category = EntityCategory.CONFIG - - class FluxICTypeSelect(FluxConfigSelect): """Representation of Flux ic type.""" @@ -202,3 +211,39 @@ class FluxRemoteConfigSelect(FluxConfigSelect): """Change the remote config setting.""" remote_config: RemoteConfig = self._name_to_state[option] await self._device.async_config_remotes(remote_config) + + +class FluxWhiteChannelSelect(FluxConfigAtStartSelect): + """Representation of Flux white channel.""" + + _attr_options = [_human_readable_option(option.name) for option in WhiteChannelType] + + def __init__( + self, + device: AIOWifiLedBulb, + entry: config_entries.ConfigEntry, + ) -> None: + """Initialize the white channel select.""" + super().__init__(device, entry) + self._attr_name = f"{entry.data[CONF_NAME]} White Channel" + if entry.unique_id: + self._attr_unique_id = f"{entry.unique_id}_white_channel" + + @property + def current_option(self) -> str | None: + """Return the current white channel type.""" + return _human_readable_option( + self.entry.data.get( + CONF_WHITE_CHANNEL_TYPE, DEFAULT_WHITE_CHANNEL_TYPE.name + ) + ) + + async def async_select_option(self, option: str) -> None: + """Change the white channel type.""" + entry = self.entry + hass = self.hass + hass.config_entries.async_update_entry( + entry, data={**entry.data, CONF_WHITE_CHANNEL_TYPE: option.lower()} + ) + # reload since we need to reinit the device + hass.async_create_task(hass.config_entries.async_reload(entry.entry_id)) diff --git a/homeassistant/components/flux_led/util.py b/homeassistant/components/flux_led/util.py index 9a1d19c66a1..18be67bf6b4 100644 --- a/homeassistant/components/flux_led/util.py +++ b/homeassistant/components/flux_led/util.py @@ -23,6 +23,10 @@ def format_as_flux_mac(mac: str | None) -> str | None: return None if mac is None else mac.replace(":", "").upper() +def _human_readable_option(const_option: str) -> str: + return const_option.replace("_", " ").title() + + def _flux_color_mode_to_hass( flux_color_mode: str | None, flux_color_modes: set[str] ) -> str: diff --git a/requirements_all.txt b/requirements_all.txt index 25871c9bebc..70760d1c917 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -681,7 +681,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.45 +flux_led==0.28.1 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6cc6d6061cb..623bb6747ae 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -424,7 +424,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.45 +flux_led==0.28.1 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/tests/components/flux_led/__init__.py b/tests/components/flux_led/__init__.py index db4aa18a096..e1abebd40f1 100644 --- a/tests/components/flux_led/__init__.py +++ b/tests/components/flux_led/__init__.py @@ -12,6 +12,7 @@ from flux_led.aio import AIOWifiLedBulb from flux_led.const import ( COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT, COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB, + WhiteChannelType, ) from flux_led.models_db import MODEL_MAP from flux_led.protocol import ( @@ -105,6 +106,7 @@ def _mocked_bulb() -> AIOWifiLedBulb: bulb.async_set_brightness = AsyncMock() bulb.async_set_device_config = AsyncMock() bulb.async_config_remotes = AsyncMock() + bulb.white_channel_channel_type = WhiteChannelType.WARM bulb.paired_remotes = 2 bulb.pixels_per_segment = 300 bulb.segments = 2 diff --git a/tests/components/flux_led/test_light.py b/tests/components/flux_led/test_light.py index 66bcf8bd212..e3a136afb0b 100644 --- a/tests/components/flux_led/test_light.py +++ b/tests/components/flux_led/test_light.py @@ -12,6 +12,7 @@ from flux_led.const import ( COLOR_MODES_RGB_W as FLUX_COLOR_MODES_RGB_W, MODE_MUSIC, MultiColorEffects, + WhiteChannelType, ) from flux_led.protocol import MusicMode import pytest @@ -25,6 +26,7 @@ from homeassistant.components.flux_led.const import ( CONF_EFFECT, CONF_SPEED_PCT, CONF_TRANSITION, + CONF_WHITE_CHANNEL_TYPE, DOMAIN, TRANSITION_JUMP, ) @@ -520,11 +522,15 @@ async def test_rgb_cct_light(hass: HomeAssistant) -> None: bulb.async_set_brightness.reset_mock() -async def test_rgbw_light(hass: HomeAssistant) -> None: - """Test an rgbw light.""" +async def test_rgbw_light_cold_white(hass: HomeAssistant) -> None: + """Test an rgbw light with a cold white channel.""" config_entry = MockConfigEntry( domain=DOMAIN, - data={CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE}, + data={ + CONF_HOST: IP_ADDRESS, + CONF_NAME: DEFAULT_ENTRY_TITLE, + CONF_WHITE_CHANNEL_TYPE: WhiteChannelType.COLD.name.lower(), + }, unique_id=MAC_ADDRESS, ) config_entry.add_to_hass(hass) @@ -622,6 +628,148 @@ async def test_rgbw_light(hass: HomeAssistant) -> None: bulb.async_set_effect.reset_mock() +async def test_rgbw_light_warm_white(hass: HomeAssistant) -> None: + """Test an rgbw light with a warm white channel.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_HOST: IP_ADDRESS, + CONF_NAME: DEFAULT_ENTRY_TITLE, + CONF_WHITE_CHANNEL_TYPE: WhiteChannelType.WARM.name.lower(), + }, + unique_id=MAC_ADDRESS, + ) + config_entry.add_to_hass(hass) + bulb = _mocked_bulb() + bulb.color_modes = {FLUX_COLOR_MODE_RGBW, FLUX_COLOR_MODE_CCT} + bulb.color_mode = FLUX_COLOR_MODE_RGBW + with _patch_discovery(), _patch_wifibulb(device=bulb): + await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) + await hass.async_block_till_done() + + entity_id = "light.bulb_rgbcw_ddeeff" + + 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] == bulb.effect_list + assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "rgbw"] + assert attributes[ATTR_RGB_COLOR] == (255, 42, 42) + + await hass.services.async_call( + LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + bulb.async_turn_off.assert_called_once() + await async_mock_device_turn_off(hass, bulb) + + 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.async_turn_on.assert_called_once() + bulb.async_turn_on.reset_mock() + bulb.is_on = True + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100}, + blocking=True, + ) + bulb.async_set_brightness.assert_called_with(100) + bulb.async_set_brightness.reset_mock() + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + { + ATTR_ENTITY_ID: entity_id, + ATTR_RGBW_COLOR: (255, 255, 255, 255), + ATTR_BRIGHTNESS: 128, + }, + blocking=True, + ) + bulb.async_set_levels.assert_called_with(128, 128, 128, 128) + bulb.async_set_levels.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_RGBW_COLOR: (255, 255, 255, 255)}, + blocking=True, + ) + bulb.async_set_levels.assert_called_with(255, 255, 255, 255) + bulb.async_set_levels.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_RGBW_COLOR: (255, 191, 178, 0)}, + blocking=True, + ) + bulb.async_set_levels.assert_called_with(255, 191, 178, 0) + bulb.async_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.async_set_white_temp.assert_called_with(6493, 255) + bulb.async_set_white_temp.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.async_set_white_temp.assert_called_with(6493, 255) + bulb.async_set_white_temp.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 290}, + blocking=True, + ) + bulb.async_set_white_temp.assert_called_with(3448, 255) + bulb.async_set_white_temp.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_RGBW_COLOR: (255, 191, 178, 0)}, + blocking=True, + ) + bulb.async_set_levels.assert_called_with(255, 191, 178, 0) + bulb.async_set_levels.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"}, + blocking=True, + ) + bulb.async_set_effect.assert_called_once() + bulb.async_set_effect.reset_mock() + + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "purple_fade", ATTR_BRIGHTNESS: 255}, + blocking=True, + ) + bulb.async_set_effect.assert_called_with("purple_fade", 50, 100) + bulb.async_set_effect.reset_mock() + + async def test_rgb_or_w_light(hass: HomeAssistant) -> None: """Test an rgb or w light.""" config_entry = MockConfigEntry( @@ -811,8 +959,8 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None: {ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154}, blocking=True, ) - bulb.async_set_levels.assert_called_with(r=0, b=0, g=0, w=0, w2=127) - bulb.async_set_levels.reset_mock() + bulb.async_set_white_temp.assert_called_with(6493, 255) + bulb.async_set_white_temp.reset_mock() await hass.services.async_call( LIGHT_DOMAIN, @@ -820,8 +968,8 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None: {ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154, ATTR_BRIGHTNESS: 255}, blocking=True, ) - bulb.async_set_levels.assert_called_with(r=0, b=0, g=0, w=0, w2=255) - bulb.async_set_levels.reset_mock() + bulb.async_set_white_temp.assert_called_with(6493, 255) + bulb.async_set_white_temp.reset_mock() await hass.services.async_call( LIGHT_DOMAIN, @@ -829,8 +977,8 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None: {ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 290}, blocking=True, ) - bulb.async_set_levels.assert_called_with(r=0, b=0, g=0, w=102, w2=25) - bulb.async_set_levels.reset_mock() + bulb.async_set_white_temp.assert_called_with(3448, 255) + bulb.async_set_white_temp.reset_mock() await hass.services.async_call( LIGHT_DOMAIN, diff --git a/tests/components/flux_led/test_select.py b/tests/components/flux_led/test_select.py index ee58a2660e3..e41ec8957ac 100644 --- a/tests/components/flux_led/test_select.py +++ b/tests/components/flux_led/test_select.py @@ -1,11 +1,16 @@ """Tests for select platform.""" from unittest.mock import patch +from flux_led.const import ( + COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT, + COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW, + WhiteChannelType, +) from flux_led.protocol import PowerRestoreState, RemoteConfig import pytest from homeassistant.components import flux_led -from homeassistant.components.flux_led.const import DOMAIN +from homeassistant.components.flux_led.const import CONF_WHITE_CHANNEL_TYPE, DOMAIN from homeassistant.components.select import DOMAIN as SELECT_DOMAIN from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, CONF_HOST, CONF_NAME from homeassistant.core import HomeAssistant @@ -191,3 +196,51 @@ async def test_select_24ghz_remote_config(hass: HomeAssistant) -> None: ) bulb.async_config_remotes.assert_called_once_with(RemoteConfig.PAIRED_ONLY) bulb.async_config_remotes.reset_mock() + + +async def test_select_white_channel_type(hass: HomeAssistant) -> None: + """Test selecting the white channel type.""" + 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.color_modes = {FLUX_COLOR_MODE_RGBW, FLUX_COLOR_MODE_CCT} + bulb.color_mode = FLUX_COLOR_MODE_RGBW + bulb.raw_state = bulb.raw_state._replace(model_num=0x06) # rgbw + with _patch_discovery(), _patch_wifibulb(device=bulb): + await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) + await hass.async_block_till_done() + + operating_mode_entity_id = "select.bulb_rgbcw_ddeeff_white_channel" + state = hass.states.get(operating_mode_entity_id) + assert state.state == WhiteChannelType.WARM.name.title() + + with pytest.raises(ValueError): + await hass.services.async_call( + SELECT_DOMAIN, + "select_option", + {ATTR_ENTITY_ID: operating_mode_entity_id, ATTR_OPTION: "INVALID"}, + blocking=True, + ) + + with patch( + "homeassistant.components.flux_led.async_setup_entry" + ) as mock_setup_entry: + await hass.services.async_call( + SELECT_DOMAIN, + "select_option", + { + ATTR_ENTITY_ID: operating_mode_entity_id, + ATTR_OPTION: WhiteChannelType.NATURAL.name.title(), + }, + blocking=True, + ) + await hass.async_block_till_done() + assert ( + config_entry.data[CONF_WHITE_CHANNEL_TYPE] + == WhiteChannelType.NATURAL.name.lower() + ) + assert len(mock_setup_entry.mock_calls) == 1