diff --git a/homeassistant/components/flux_led/entity.py b/homeassistant/components/flux_led/entity.py index 1589bd0fb3a..6296f11b21a 100644 --- a/homeassistant/components/flux_led/entity.py +++ b/homeassistant/components/flux_led/entity.py @@ -76,6 +76,11 @@ class FluxEntity(CoordinatorEntity): unique_id, self._device, coordinator.entry ) + async def _async_ensure_device_on(self) -> None: + """Turn the device on if it needs to be turned on before a command.""" + if self._device.requires_turn_on and not self._device.is_on: + await self._device.async_turn_on() + @property def extra_state_attributes(self) -> dict[str, str]: """Return the attributes.""" diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index 6bde2ec3b31..5d8da698a9a 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -6,6 +6,7 @@ import logging from typing import Any, Final from flux_led.const import MultiColorEffects +from flux_led.protocol import MusicMode from flux_led.utils import ( color_temp_to_white_levels, rgbcw_brightness, @@ -73,6 +74,11 @@ MODE_ATTRS = { ATTR_WHITE, } +ATTR_FOREGROUND_COLOR: Final = "foreground_color" +ATTR_BACKGROUND_COLOR: Final = "background_color" +ATTR_SENSITIVITY: Final = "sensitivity" +ATTR_LIGHT_SCREEN: Final = "light_screen" + # Constant color temp values for 2 flux_led special modes # Warm-white and Cool-white modes COLOR_TEMP_WARM_VS_COLD_WHITE_CUT_OFF: Final = 285 @@ -81,6 +87,7 @@ EFFECT_CUSTOM: Final = "custom" SERVICE_CUSTOM_EFFECT: Final = "set_custom_effect" SERVICE_SET_ZONES: Final = "set_zones" +SERVICE_SET_MUSIC_MODE: Final = "set_music_mode" CUSTOM_EFFECT_DICT: Final = { vol.Required(CONF_COLORS): vol.All( @@ -96,6 +103,25 @@ CUSTOM_EFFECT_DICT: Final = { ), } +SET_MUSIC_MODE_DICT: Final = { + vol.Optional(ATTR_SENSITIVITY, default=100): vol.All( + vol.Range(min=0, max=100), vol.Coerce(int) + ), + vol.Optional(ATTR_BRIGHTNESS, default=100): vol.All( + vol.Range(min=0, max=100), vol.Coerce(int) + ), + vol.Optional(ATTR_EFFECT, default=1): vol.All( + vol.Range(min=1, max=16), vol.Coerce(int) + ), + vol.Optional(ATTR_LIGHT_SCREEN, default=False): bool, + vol.Optional(ATTR_FOREGROUND_COLOR): vol.All( + vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 3) + ), + vol.Optional(ATTR_BACKGROUND_COLOR): vol.All( + vol.Coerce(tuple), vol.ExactSequence((cv.byte,) * 3) + ), +} + SET_ZONES_DICT: Final = { vol.Required(CONF_COLORS): vol.All( cv.ensure_list, @@ -130,6 +156,11 @@ async def async_setup_entry( SET_ZONES_DICT, "async_set_zones", ) + platform.async_register_entity_service( + SERVICE_SET_MUSIC_MODE, + SET_MUSIC_MODE_DICT, + "async_set_music_mode", + ) options = entry.options try: @@ -330,3 +361,23 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): speed_pct, _str_to_multi_color_effect(effect), ) + + async def async_set_music_mode( + self, + sensitivity: int, + brightness: int, + effect: int, + light_screen: bool, + foreground_color: tuple[int, int, int] | None = None, + background_color: tuple[int, int, int] | None = None, + ) -> None: + """Configure music mode.""" + await self._async_ensure_device_on() + await self._device.async_set_music_mode( + sensitivity=sensitivity, + brightness=brightness, + mode=MusicMode.LIGHT_SCREEN.value if light_screen else None, + effect=effect, + foreground_color=foreground_color, + background_color=background_color, + ) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 51498f99590..ab1e78bc1ed 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.8"], + "requirements": ["flux_led==0.27.10"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/homeassistant/components/flux_led/services.yaml b/homeassistant/components/flux_led/services.yaml index ff1e0679807..8a16f456311 100644 --- a/homeassistant/components/flux_led/services.yaml +++ b/homeassistant/components/flux_led/services.yaml @@ -77,3 +77,60 @@ set_zones: - "strobe" - "jump" - "breathing" +set_music_mode: + description: Configure music mode on Controller RGB with MIC (0x08), Addressable v2 (0xA2), and Addressable v3 (0xA3) devices that have a built-in microphone. + target: + entity: + integration: flux_led + domain: light + fields: + sensitivity: + description: Microphone sensitivity (0-100) + example: 80 + default: 100 + required: false + selector: + number: + min: 1 + step: 1 + max: 100 + unit_of_measurement: "%" + brightness: + description: Light brightness (0-100) + example: 80 + default: 100 + required: false + selector: + number: + min: 1 + step: 1 + max: 100 + unit_of_measurement: "%" + light_screen: + description: Light screen mode for 2 dimensional pixels (Addressable models only) + default: false + required: false + selector: + boolean: + effect: + description: Effect (1-16 on Addressable models, 0-3 on RGB with MIC models) + example: 1 + default: 1 + required: false + selector: + number: + min: 0 + step: 1 + max: 16 + foreground_color: + description: The foreground RGB color + example: "[255, 100, 100]" + required: false + selector: + object: + background_color: + description: The background RGB color (Addressable models only) + example: "[255, 100, 100]" + required: false + selector: + object: diff --git a/homeassistant/components/flux_led/switch.py b/homeassistant/components/flux_led/switch.py index 37f5f057353..8acb5e8861a 100644 --- a/homeassistant/components/flux_led/switch.py +++ b/homeassistant/components/flux_led/switch.py @@ -126,8 +126,7 @@ class FluxMusicSwitch(FluxEntity, SwitchEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the microphone on.""" - if self._device.requires_turn_on and not self._device.is_on: - await self._device.async_turn_on() + await self._async_ensure_device_on() await self._device.async_set_music_mode() self.async_write_ha_state() await self.coordinator.async_request_refresh() diff --git a/requirements_all.txt b/requirements_all.txt index b4e018cb7ca..2734e6309b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -664,7 +664,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.8 +flux_led==0.27.10 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a20c95e1603..102bf1d3e5f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -405,7 +405,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.8 +flux_led==0.27.10 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/tests/components/flux_led/test_light.py b/tests/components/flux_led/test_light.py index cdf11a19a0b..66bcf8bd212 100644 --- a/tests/components/flux_led/test_light.py +++ b/tests/components/flux_led/test_light.py @@ -10,8 +10,10 @@ from flux_led.const import ( COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW, COLOR_MODE_RGBWW as FLUX_COLOR_MODE_RGBWW, COLOR_MODES_RGB_W as FLUX_COLOR_MODES_RGB_W, + MODE_MUSIC, MultiColorEffects, ) +from flux_led.protocol import MusicMode import pytest from homeassistant.components import flux_led @@ -26,6 +28,12 @@ from homeassistant.components.flux_led.const import ( DOMAIN, TRANSITION_JUMP, ) +from homeassistant.components.flux_led.light import ( + ATTR_BACKGROUND_COLOR, + ATTR_FOREGROUND_COLOR, + ATTR_LIGHT_SCREEN, + ATTR_SENSITIVITY, +) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_MODE, @@ -1191,3 +1199,47 @@ async def test_addressable_light(hass: HomeAssistant) -> None: bulb.async_turn_on.assert_called_once() bulb.async_turn_on.reset_mock() await async_mock_device_turn_on(hass, bulb) + + +async def test_music_mode_service(hass: HomeAssistant) -> None: + """Test music mode service.""" + 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=0xA3) # has music mode + bulb.microphone = True + 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" + assert hass.states.get(entity_id) + + bulb.effect = MODE_MUSIC + bulb.is_on = False + await hass.services.async_call( + DOMAIN, + "set_music_mode", + { + ATTR_ENTITY_ID: entity_id, + ATTR_EFFECT: 12, + ATTR_LIGHT_SCREEN: True, + ATTR_SENSITIVITY: 50, + ATTR_BRIGHTNESS: 50, + ATTR_FOREGROUND_COLOR: [255, 0, 0], + ATTR_BACKGROUND_COLOR: [0, 255, 0], + }, + blocking=True, + ) + bulb.async_set_music_mode.assert_called_once_with( + sensitivity=50, + brightness=50, + mode=MusicMode.LIGHT_SCREEN.value, + effect=12, + foreground_color=(255, 0, 0), + background_color=(0, 255, 0), + )