From cb3b19b0002e2aeb72738c90fd495cfaf5a3c5f8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 22 Nov 2021 18:52:06 -0600 Subject: [PATCH] Add support for adjusting flux_led effects speed (#59679) --- homeassistant/components/flux_led/__init__.py | 5 +- homeassistant/components/flux_led/const.py | 26 ++ homeassistant/components/flux_led/entity.py | 46 ++-- homeassistant/components/flux_led/light.py | 42 +--- .../components/flux_led/manifest.json | 2 +- homeassistant/components/flux_led/number.py | 79 ++++++ homeassistant/components/flux_led/switch.py | 4 +- homeassistant/components/flux_led/util.py | 27 +++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/flux_led/__init__.py | 13 + tests/components/flux_led/test_number.py | 227 ++++++++++++++++++ 12 files changed, 414 insertions(+), 61 deletions(-) create mode 100644 homeassistant/components/flux_led/number.py create mode 100644 homeassistant/components/flux_led/util.py create mode 100644 tests/components/flux_led/test_number.py diff --git a/homeassistant/components/flux_led/__init__.py b/homeassistant/components/flux_led/__init__.py index 717a3c9e2b0..db73105e8ce 100644 --- a/homeassistant/components/flux_led/__init__.py +++ b/homeassistant/components/flux_led/__init__.py @@ -35,7 +35,10 @@ from .const import ( _LOGGER = logging.getLogger(__name__) -PLATFORMS_BY_TYPE: Final = {DeviceType.Bulb: ["light"], DeviceType.Switch: ["switch"]} +PLATFORMS_BY_TYPE: Final = { + DeviceType.Bulb: ["light", "number"], + DeviceType.Switch: ["switch"], +} DISCOVERY_INTERVAL: Final = timedelta(minutes=15) REQUEST_REFRESH_DELAY: Final = 1.5 diff --git a/homeassistant/components/flux_led/const.py b/homeassistant/components/flux_led/const.py index e7f9509c54b..1bc9bb6d227 100644 --- a/homeassistant/components/flux_led/const.py +++ b/homeassistant/components/flux_led/const.py @@ -4,8 +4,31 @@ import asyncio import socket from typing import Final +from flux_led.const import ( + COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT, + 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 homeassistant.components.light import ( + COLOR_MODE_COLOR_TEMP, + COLOR_MODE_RGB, + COLOR_MODE_RGBW, + COLOR_MODE_RGBWW, +) + DOMAIN: Final = "flux_led" + +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, +} + + API: Final = "flux_api" SIGNAL_STATE_UPDATED = "flux_led_{}_state_updated" @@ -48,6 +71,9 @@ CONF_SPEED_PCT: Final = "speed_pct" CONF_TRANSITION: Final = "transition" +EFFECT_SUPPORT_MODES = {COLOR_MODE_RGB, COLOR_MODE_RGBW, COLOR_MODE_RGBWW} + + CONF_CUSTOM_EFFECT_COLORS: Final = "custom_effect_colors" CONF_CUSTOM_EFFECT_SPEED_PCT: Final = "custom_effect_speed_pct" CONF_CUSTOM_EFFECT_TRANSITION: Final = "custom_effect_transition" diff --git a/homeassistant/components/flux_led/entity.py b/homeassistant/components/flux_led/entity.py index 4183ccc14cd..bab9a24bfe5 100644 --- a/homeassistant/components/flux_led/entity.py +++ b/homeassistant/components/flux_led/entity.py @@ -42,32 +42,11 @@ class FluxEntity(CoordinatorEntity): sw_version=str(self._device.version_num), ) - @property - def is_on(self) -> bool: - """Return true if device is on.""" - return cast(bool, self._device.is_on) - @property def extra_state_attributes(self) -> dict[str, str]: """Return the attributes.""" return {"ip_address": self._device.ipaddr} - async def async_turn_on(self, **kwargs: Any) -> None: - """Turn the specified device on.""" - await self._async_turn_on(**kwargs) - self.async_write_ha_state() - await self.coordinator.async_request_refresh() - - @abstractmethod - async def _async_turn_on(self, **kwargs: Any) -> None: - """Turn the specified device on.""" - - async def async_turn_off(self, **kwargs: Any) -> None: - """Turn the specified device off.""" - await self._device.async_turn_off() - self.async_write_ha_state() - await self.coordinator.async_request_refresh() - @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" @@ -85,3 +64,28 @@ class FluxEntity(CoordinatorEntity): ) ) await super().async_added_to_hass() + + +class FluxOnOffEntity(FluxEntity): + """Representation of a Flux entity that supports on/off.""" + + @property + def is_on(self) -> bool: + """Return true if device is on.""" + return cast(bool, self._device.is_on) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the specified device on.""" + await self._async_turn_on(**kwargs) + self.async_write_ha_state() + await self.coordinator.async_request_refresh() + + @abstractmethod + async def _async_turn_on(self, **kwargs: Any) -> None: + """Turn the specified device on.""" + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the specified device off.""" + await self._device.async_turn_off() + self.async_write_ha_state() + await self.coordinator.async_request_refresh() diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index fedf31743da..878c27aac8c 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -6,13 +6,6 @@ import logging import random from typing import Any, Final, cast -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.utils import ( color_temp_to_white_levels, rgbcw_brightness, @@ -33,7 +26,6 @@ from homeassistant.components.light import ( ATTR_WHITE, COLOR_MODE_BRIGHTNESS, COLOR_MODE_COLOR_TEMP, - COLOR_MODE_ONOFF, COLOR_MODE_RGB, COLOR_MODE_RGBW, COLOR_MODE_RGBWW, @@ -78,6 +70,7 @@ from .const import ( CONF_TRANSITION, DEFAULT_EFFECT_SPEED, DOMAIN, + EFFECT_SUPPORT_MODES, FLUX_HOST, FLUX_LED_DISCOVERY, FLUX_MAC, @@ -89,22 +82,13 @@ from .const import ( TRANSITION_JUMP, TRANSITION_STROBE, ) -from .entity import FluxEntity +from .entity import FluxOnOffEntity +from .util import _flux_color_mode_to_hass, _hass_color_modes _LOGGER = logging.getLogger(__name__) SUPPORT_FLUX_LED: Final = 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, -} - -EFFECT_SUPPORT_MODES = {COLOR_MODE_RGB, COLOR_MODE_RGBW, COLOR_MODE_RGBWW} - # 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 @@ -148,15 +132,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) -def _flux_color_mode_to_hass(flux_color_mode: str, flux_color_modes: set[str]) -> str: - """Map the flux color mode to Home Assistant color mode.""" - if flux_color_mode == FLUX_COLOR_MODE_DIM: - if len(flux_color_modes) > 1: - return COLOR_MODE_WHITE - return COLOR_MODE_BRIGHTNESS - return FLUX_COLOR_MODE_TO_HASS.get(flux_color_mode, COLOR_MODE_ONOFF) - - async def async_setup_platform( hass: HomeAssistant, config: ConfigType, @@ -242,7 +217,7 @@ async def async_setup_entry( ) -class FluxLight(FluxEntity, CoordinatorEntity, LightEntity): +class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): """Representation of a Flux light.""" def __init__( @@ -261,10 +236,7 @@ class FluxLight(FluxEntity, CoordinatorEntity, LightEntity): 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 = { - _flux_color_mode_to_hass(mode, self._device.color_modes) - for mode in self._device.color_modes - } + self._attr_supported_color_modes = _hass_color_modes(self._device) if self._attr_supported_color_modes.intersection(EFFECT_SUPPORT_MODES): self._attr_supported_features |= SUPPORT_EFFECT self._attr_effect_list = [*self._device.effect_list, EFFECT_RANDOM] @@ -405,7 +377,9 @@ class FluxLight(FluxEntity, CoordinatorEntity, LightEntity): self._custom_effect_transition, ) return - await self._device.async_set_effect(effect, DEFAULT_EFFECT_SPEED) + await self._device.async_set_effect( + effect, self._device.speed or DEFAULT_EFFECT_SPEED + ) return # Handle brightness adjustment in CCT Color Mode diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 0b9b7f00e8c..d1af244ad83 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.24.24"], + "requirements": ["flux_led==0.24.27"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/homeassistant/components/flux_led/number.py b/homeassistant/components/flux_led/number.py new file mode 100644 index 00000000000..9ae581d8c88 --- /dev/null +++ b/homeassistant/components/flux_led/number.py @@ -0,0 +1,79 @@ +"""Support for LED numbers.""" +from __future__ import annotations + +from typing import cast + +from homeassistant import config_entries +from homeassistant.components.number import NumberEntity +from homeassistant.components.number.const import MODE_SLIDER +from homeassistant.const import CONF_NAME +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import FluxLedUpdateCoordinator +from .const import DOMAIN, EFFECT_SUPPORT_MODES +from .entity import FluxEntity +from .util import _hass_color_modes + + +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] + + color_modes = _hass_color_modes(coordinator.device) + if not color_modes.intersection(EFFECT_SUPPORT_MODES): + return + + async_add_entities( + [ + FluxNumber( + coordinator, + entry.unique_id, + entry.data[CONF_NAME], + ) + ] + ) + + +class FluxNumber(FluxEntity, CoordinatorEntity, NumberEntity): + """Defines a flux_led speed number.""" + + _attr_min_value = 1 + _attr_max_value = 100 + _attr_step = 1 + _attr_mode = MODE_SLIDER + _attr_icon = "mdi:speedometer" + + def __init__( + self, + coordinator: FluxLedUpdateCoordinator, + unique_id: str | None, + name: str, + ) -> None: + """Initialize the flux number.""" + super().__init__(coordinator, unique_id, name) + self._attr_name = f"{name} Effect Speed" + + @property + def value(self) -> float: + """Return the effect speed.""" + return cast(float, self._device.speed) + + async def async_set_value(self, value: float) -> None: + """Set the flux speed value.""" + current_effect = self._device.effect + new_speed = int(value) + if not current_effect: + raise HomeAssistantError( + "Speed can only be adjusted when an effect is active" + ) + if self._device.original_addressable and not self._device.is_on: + raise HomeAssistantError("Speed can only be adjusted when the light is on") + await self._device.async_set_effect(current_effect, new_speed) + await self.coordinator.async_request_refresh() diff --git a/homeassistant/components/flux_led/switch.py b/homeassistant/components/flux_led/switch.py index 0ca7a771c78..d022acc1c74 100644 --- a/homeassistant/components/flux_led/switch.py +++ b/homeassistant/components/flux_led/switch.py @@ -12,7 +12,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import FluxLedUpdateCoordinator from .const import DOMAIN -from .entity import FluxEntity +from .entity import FluxOnOffEntity async def async_setup_entry( @@ -33,7 +33,7 @@ async def async_setup_entry( ) -class FluxSwitch(FluxEntity, CoordinatorEntity, SwitchEntity): +class FluxSwitch(FluxOnOffEntity, CoordinatorEntity, SwitchEntity): """Representation of a Flux switch.""" async def _async_turn_on(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/flux_led/util.py b/homeassistant/components/flux_led/util.py new file mode 100644 index 00000000000..a564cb81bd0 --- /dev/null +++ b/homeassistant/components/flux_led/util.py @@ -0,0 +1,27 @@ +"""Utils for FluxLED/MagicHome.""" +from __future__ import annotations + +from flux_led.aio import AIOWifiLedBulb +from flux_led.const import COLOR_MODE_DIM as FLUX_COLOR_MODE_DIM + +from homeassistant.components.light import ( + COLOR_MODE_BRIGHTNESS, + COLOR_MODE_ONOFF, + COLOR_MODE_WHITE, +) + +from .const import FLUX_COLOR_MODE_TO_HASS + + +def _hass_color_modes(device: AIOWifiLedBulb) -> set[str]: + color_modes = device.color_modes + return {_flux_color_mode_to_hass(mode, color_modes) for mode in color_modes} + + +def _flux_color_mode_to_hass(flux_color_mode: str, flux_color_modes: set[str]) -> str: + """Map the flux color mode to Home Assistant color mode.""" + if flux_color_mode == FLUX_COLOR_MODE_DIM: + if len(flux_color_modes) > 1: + return COLOR_MODE_WHITE + return COLOR_MODE_BRIGHTNESS + return FLUX_COLOR_MODE_TO_HASS.get(flux_color_mode, COLOR_MODE_ONOFF) diff --git a/requirements_all.txt b/requirements_all.txt index 5a6f14b3726..c337ff932eb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -658,7 +658,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.24.24 +flux_led==0.24.27 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 313ae45a336..8e93e6873f0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.24.24 +flux_led==0.24.27 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/tests/components/flux_led/__init__.py b/tests/components/flux_led/__init__.py index 19e11e6085c..c04189312af 100644 --- a/tests/components/flux_led/__init__.py +++ b/tests/components/flux_led/__init__.py @@ -67,8 +67,11 @@ def _mocked_bulb() -> AIOWifiLedBulb: bulb.brightness = 128 bulb.model_num = 0x35 bulb.effect = None + bulb.speed = 50 bulb.model = "Smart Bulb (0x35)" bulb.version_num = 8 + bulb.original_addressable = False + bulb.addressable = False bulb.rgbwcapable = True bulb.color_modes = {FLUX_COLOR_MODE_RGB, FLUX_COLOR_MODE_CCT} bulb.color_mode = FLUX_COLOR_MODE_RGB @@ -115,6 +118,16 @@ async def async_mock_device_turn_on(hass: HomeAssistant, bulb: AIOWifiLedBulb) - await hass.async_block_till_done() +async def async_mock_effect_speed( + hass: HomeAssistant, bulb: AIOWifiLedBulb, effect: str, speed: int +) -> None: + """Mock the device being on with an effect.""" + bulb.speed = speed + bulb.effect = effect + bulb.data_receive_callback() + await hass.async_block_till_done() + + def _patch_discovery(device=None, no_device=False): async def _discovery(*args, **kwargs): if no_device: diff --git a/tests/components/flux_led/test_number.py b/tests/components/flux_led/test_number.py new file mode 100644 index 00000000000..6e7f9e60de6 --- /dev/null +++ b/tests/components/flux_led/test_number.py @@ -0,0 +1,227 @@ +"""Tests for the flux_led number platform.""" + + +from flux_led.const import COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB +import pytest + +from homeassistant.components import flux_led +from homeassistant.components.flux_led.const import DOMAIN +from homeassistant.components.number import ( + ATTR_VALUE, + DOMAIN as NUMBER_DOMAIN, + SERVICE_SET_VALUE, +) +from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, STATE_ON +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er +from homeassistant.setup import async_setup_component + +from . import ( + DEFAULT_ENTRY_TITLE, + IP_ADDRESS, + MAC_ADDRESS, + _mocked_bulb, + _patch_discovery, + _patch_wifibulb, + async_mock_device_turn_off, + async_mock_device_turn_on, + async_mock_effect_speed, +) + +from tests.common import MockConfigEntry + + +async def test_number_unique_id(hass: HomeAssistant) -> None: + """Test a number unique id.""" + 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() + 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 = "number.az120444_aabbccddeeff_effect_speed" + entity_registry = er.async_get(hass) + assert entity_registry.async_get(entity_id).unique_id == MAC_ADDRESS + + +async def test_rgb_light_effect_speed(hass: HomeAssistant) -> None: + """Test an rgb light with an effect.""" + 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_RGB} + 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() + + await async_mock_device_turn_on(hass, bulb) + + light_entity_id = "light.az120444_aabbccddeeff" + number_entity_id = "number.az120444_aabbccddeeff_effect_speed" + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: number_entity_id, ATTR_VALUE: 100}, + blocking=True, + ) + + state = hass.states.get(light_entity_id) + assert state.state == STATE_ON + + bulb.effect = "colorloop" + bulb.speed = 50 + await async_mock_device_turn_off(hass, bulb) + state = hass.states.get(number_entity_id) + assert state.state == "50" + + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: number_entity_id, ATTR_VALUE: 100}, + blocking=True, + ) + bulb.async_set_effect.assert_called_with("colorloop", 100) + bulb.async_set_effect.reset_mock() + + await async_mock_effect_speed(hass, bulb, "red_fade", 50) + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: number_entity_id, ATTR_VALUE: 50}, + blocking=True, + ) + bulb.async_set_effect.assert_called_with("red_fade", 50) + bulb.async_set_effect.reset_mock() + + state = hass.states.get(number_entity_id) + assert state.state == "50" + + +async def test_original_addressable_light_effect_speed(hass: HomeAssistant) -> None: + """Test an original addressable light with an effect.""" + 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.original_addressable = True + bulb.raw_state = bulb.raw_state._replace( + model_num=0xA1 + ) # Original addressable model + bulb.color_modes = {FLUX_COLOR_MODE_RGB} + bulb.color_mode = FLUX_COLOR_MODE_RGB + bulb.effect = "7 colors change gradually" + bulb.speed = 50 + 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() + + await async_mock_device_turn_on(hass, bulb) + + light_entity_id = "light.az120444_aabbccddeeff" + number_entity_id = "number.az120444_aabbccddeeff_effect_speed" + + state = hass.states.get(light_entity_id) + assert state.state == STATE_ON + + state = hass.states.get(number_entity_id) + assert state.state == "50" + + await async_mock_device_turn_off(hass, bulb) + + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: number_entity_id, ATTR_VALUE: 100}, + blocking=True, + ) + + await async_mock_device_turn_on(hass, bulb) + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: number_entity_id, ATTR_VALUE: 100}, + blocking=True, + ) + bulb.async_set_effect.assert_called_with("7 colors change gradually", 100) + bulb.async_set_effect.reset_mock() + await async_mock_effect_speed(hass, bulb, "7 colors run in olivary", 100) + + state = hass.states.get(number_entity_id) + assert state.state == "100" + + +async def test_addressable_light_effect_speed(hass: HomeAssistant) -> None: + """Test an addressable light with an effect.""" + 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.addressable = True + bulb.raw_state = bulb.raw_state._replace( + model_num=0xA2 + ) # Original addressable model + bulb.color_modes = {FLUX_COLOR_MODE_RGB} + bulb.color_mode = FLUX_COLOR_MODE_RGB + bulb.effect = "RBM 1" + bulb.speed = 50 + 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() + + await async_mock_device_turn_on(hass, bulb) + + light_entity_id = "light.az120444_aabbccddeeff" + number_entity_id = "number.az120444_aabbccddeeff_effect_speed" + + state = hass.states.get(light_entity_id) + assert state.state == STATE_ON + + state = hass.states.get(number_entity_id) + assert state.state == "50" + + await async_mock_device_turn_off(hass, bulb) + + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: number_entity_id, ATTR_VALUE: 100}, + blocking=True, + ) + bulb.async_set_effect.assert_called_with("RBM 1", 100) + bulb.async_set_effect.reset_mock() + + await async_mock_device_turn_on(hass, bulb) + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: number_entity_id, ATTR_VALUE: 100}, + blocking=True, + ) + bulb.async_set_effect.assert_called_with("RBM 1", 100) + bulb.async_set_effect.reset_mock() + await async_mock_effect_speed(hass, bulb, "RBM 2", 100) + + state = hass.states.get(number_entity_id) + assert state.state == "100"