1515from homeassistant .components .light import ( # type: ignore[attr-defined]
1616 ATTR_BRIGHTNESS ,
1717 ATTR_COLOR_TEMP_KELVIN ,
18+ ATTR_EFFECT ,
1819 ATTR_RGB_COLOR ,
1920 ColorMode ,
2021 LightEntity ,
2627from homeassistant .helpers .restore_state import RestoreEntity
2728
2829from .const import (
30+ CONF_ENABLE_SCENES ,
31+ DEFAULT_ENABLE_SCENES ,
2932 SEGMENT_MODE_GROUPED ,
3033 SEGMENT_MODE_INDIVIDUAL ,
3134)
3841 GoveeDevice ,
3942 PowerCommand ,
4043 RGBColor ,
44+ SceneCommand ,
4145)
4246from .platforms .grouped_segment import GoveeGroupedSegmentEntity
4347from .platforms .segment import GoveeSegmentEntity
@@ -61,10 +65,13 @@ async def async_setup_entry(
6165 # Get per-device segment modes
6266 device_modes = entry .options .get ("segment_mode_by_device" , {})
6367
68+ # Check if scenes are enabled in options
69+ enable_scenes = entry .options .get (CONF_ENABLE_SCENES , DEFAULT_ENABLE_SCENES )
70+
6471 for device in coordinator .devices .values ():
6572 # Only create light entities for devices with power control (not fans)
6673 if device .supports_power and not device .is_fan :
67- entities .append (GoveeLightEntity (coordinator , device ))
74+ entities .append (GoveeLightEntity (coordinator , device , enable_scenes ))
6875
6976 # Create segment entities for RGBIC devices based on per-device mode
7077 if device .supports_segments and device .segment_count > 0 :
@@ -124,6 +131,7 @@ def __init__(
124131 self ,
125132 coordinator : GoveeCoordinator ,
126133 device : GoveeDevice ,
134+ enable_scenes : bool = True ,
127135 ) -> None :
128136 """Initialize the light entity."""
129137 super ().__init__ (coordinator , device )
@@ -138,10 +146,16 @@ def __init__(
138146 # Get device brightness range
139147 self ._brightness_min , self ._brightness_max = device .brightness_range
140148
141- # Add effect support if device has scenes
142- if device .supports_scenes :
149+ # Effect support: only if device has scenes AND scenes are enabled
150+ self ._enable_scenes = device .supports_scenes and enable_scenes
151+ if self ._enable_scenes :
143152 self ._attr_supported_features = LightEntityFeature .EFFECT
144153
154+ # Scene-to-effect mappings (populated in async_added_to_hass)
155+ self ._effect_to_scene : dict [str , tuple [int , str ]] = {}
156+ self ._scene_id_to_effect : dict [str , str ] = {}
157+ self ._effect_names : list [str ] = []
158+
145159 def _determine_color_modes (self ) -> set [ColorMode ]:
146160 """Determine supported color modes from device capabilities."""
147161 modes : set [ColorMode ] = set ()
@@ -220,6 +234,24 @@ def max_color_temp_kelvin(self) -> int:
220234 temp_range = self ._device .color_temp_range
221235 return temp_range .max_kelvin if temp_range else 9000
222236
237+ @property
238+ def effect_list (self ) -> list [str ] | None :
239+ """Return list of available effects (scene names)."""
240+ return self ._effect_names if self ._effect_names else None
241+
242+ @property
243+ def effect (self ) -> str | None :
244+ """Return currently active effect (scene name)."""
245+ state = self .device_state
246+ if not state or not state .active_scene :
247+ return None
248+ # Look up display name from scene ID mapping
249+ effect_name = self ._scene_id_to_effect .get (state .active_scene )
250+ if effect_name :
251+ return effect_name
252+ # Fall back to stored scene name if ID not in mapping
253+ return state .active_scene_name
254+
223255 def _ha_to_device_brightness (self , ha_brightness : int ) -> int :
224256 """Convert HA brightness (0-255) to device range, respecting min."""
225257 ratio = ha_brightness / HA_BRIGHTNESS_MAX
@@ -233,11 +265,29 @@ def _device_to_ha_brightness(self, device_brightness: int) -> int:
233265 if device_range <= 0 :
234266 return 0
235267 return int (
236- (device_brightness - self ._brightness_min ) / device_range * HA_BRIGHTNESS_MAX
268+ (device_brightness - self ._brightness_min )
269+ / device_range
270+ * HA_BRIGHTNESS_MAX
237271 )
238272
239273 async def async_turn_on (self , ** kwargs : Any ) -> None :
240274 """Turn the light on with optional parameters."""
275+ # Handle effect (scene activation)
276+ if ATTR_EFFECT in kwargs :
277+ effect_name = kwargs [ATTR_EFFECT ]
278+ scene_info = self ._effect_to_scene .get (effect_name )
279+ if scene_info :
280+ scene_id , scene_name = scene_info
281+ await self .coordinator .async_control_device (
282+ self ._device_id ,
283+ SceneCommand (scene_id = scene_id , scene_name = scene_name ),
284+ )
285+ else :
286+ _LOGGER .warning (
287+ "Unknown effect '%s' for %s" , effect_name , self ._device .name
288+ )
289+ return
290+
241291 # Handle brightness
242292 if ATTR_BRIGHTNESS in kwargs :
243293 ha_brightness = kwargs [ATTR_BRIGHTNESS ]
@@ -289,8 +339,35 @@ async def async_turn_off(self, **kwargs: Any) -> None:
289339 PowerCommand (power_on = False ),
290340 )
291341
342+ def _build_effect_mapping (self , scenes : list [dict [str , Any ]]) -> None :
343+ """Build effect name mappings from scene data.
344+
345+ Handles duplicate scene names by appending a counter,
346+ mirroring the logic in GoveeSceneSelectEntity.
347+ """
348+ self ._effect_to_scene = {}
349+ self ._scene_id_to_effect = {}
350+ names : list [str ] = []
351+
352+ for scene_data in scenes :
353+ scene_id = scene_data .get ("value" , {}).get ("id" , 0 )
354+ scene_name = scene_data .get ("name" , f"Scene { scene_id } " )
355+
356+ # Handle duplicate names by appending counter
357+ unique_name = scene_name
358+ counter = 1
359+ while unique_name in self ._effect_to_scene :
360+ unique_name = f"{ scene_name } ({ counter } )"
361+ counter += 1
362+
363+ self ._effect_to_scene [unique_name ] = (scene_id , scene_name )
364+ self ._scene_id_to_effect [str (scene_id )] = unique_name
365+ names .append (unique_name )
366+
367+ self ._effect_names = names
368+
292369 async def async_added_to_hass (self ) -> None :
293- """Restore state for group devices."""
370+ """Restore state for group devices and load scenes for effects ."""
294371 await super ().async_added_to_hass ()
295372
296373 if self ._device .is_group :
@@ -304,3 +381,9 @@ async def async_added_to_hass(self) -> None:
304381 last_state .attributes ["brightness" ]
305382 )
306383 self .coordinator .restore_group_state (self ._device_id , power , brightness )
384+
385+ # Load scenes for effect support (skip group devices - no scene API support)
386+ if self ._enable_scenes and not self ._device .is_group :
387+ scenes = await self .coordinator .async_get_scenes (self ._device_id )
388+ if scenes :
389+ self ._build_effect_mapping (scenes )
0 commit comments