Fix Yeelight ambilight effect state (#178787)

This commit is contained in:
Pedro Miguel Cruz
2026-08-25 12:41:01 +02:00
committed by GitHub
parent 1db30fa5d3
commit 52e76e7a05
3 changed files with 96 additions and 15 deletions
@@ -12,7 +12,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_call_later
from .const import (
ACTIVE_COLOR_FLOWING,
ACTIVE_MODE_NIGHTLIGHT,
DATA_UPDATED,
STATE_CHANGE_TIME,
@@ -142,19 +141,10 @@ class YeelightDevice:
return False
@property
def is_color_flow_enabled(self) -> bool:
"""Return true / false if color flow is currently running."""
return self._color_flow and int(self._color_flow) == ACTIVE_COLOR_FLOWING
@property
def _active_mode(self):
return self.bulb.last_properties.get("active_mode")
@property
def _color_flow(self):
return self.bulb.last_properties.get("flowing")
@property
def _nightlight_brightness(self):
return self.bulb.last_properties.get("nl_br")
+14 -5
View File
@@ -40,6 +40,7 @@ from homeassistant.util import color as color_util
from . import YEELIGHT_FLOW_TRANSITION_SCHEMA, YeelightConfigEntry
from .const import (
ACTION_RECOVER,
ACTIVE_COLOR_FLOWING,
ATTR_ACTION,
ATTR_COUNT,
ATTR_MODE_MUSIC,
@@ -549,7 +550,12 @@ class YeelightBaseLight(YeelightEntity, LightEntity):
@override
def effect(self) -> str | None:
"""Return the current effect."""
return self._effect if self.device.is_color_flow_enabled else None
return self._effect if self._is_color_flow_enabled else None
@property
def _is_color_flow_enabled(self) -> bool:
color_flow = self._get_property("flowing")
return bool(color_flow) and int(color_flow) == ACTIVE_COLOR_FLOWING
@property
def _bulb(self) -> AsyncBulb:
@@ -583,7 +589,7 @@ class YeelightBaseLight(YeelightEntity, LightEntity):
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the device specific state attributes."""
attributes = {
"flowing": self.device.is_color_flow_enabled,
"flowing": self._is_color_flow_enabled,
"music_mode": self._bulb.music_mode,
}
@@ -655,7 +661,7 @@ class YeelightBaseLight(YeelightEntity, LightEntity):
):
return
if (
not self.device.is_color_flow_enabled
not self._is_color_flow_enabled
and self.color_mode == ColorMode.HS
and self.hs_color == hs_color
):
@@ -680,7 +686,7 @@ class YeelightBaseLight(YeelightEntity, LightEntity):
):
return
if (
not self.device.is_color_flow_enabled
not self._is_color_flow_enabled
and self.color_mode == ColorMode.RGB
and self.rgb_color == rgb
):
@@ -706,7 +712,7 @@ class YeelightBaseLight(YeelightEntity, LightEntity):
return
if (
not self.device.is_color_flow_enabled
not self._is_color_flow_enabled
and self.color_mode == ColorMode.COLOR_TEMP
and self.color_temp_kelvin == temp_in_k
):
@@ -764,6 +770,8 @@ class YeelightBaseLight(YeelightEntity, LightEntity):
if effect == EFFECT_STOP:
await self._bulb.async_stop_flow(light_type=self.light_type)
self._effect = None
await self.device.async_update(True)
return
if effect in self.custom_effects_names:
@@ -783,6 +791,7 @@ class YeelightBaseLight(YeelightEntity, LightEntity):
await self._bulb.async_start_flow(flow, light_type=self.light_type)
self._effect = effect
await self.device.async_update(True)
@_async_cmd
async def _async_turn_on(self, duration) -> None:
+82
View File
@@ -57,6 +57,7 @@ from homeassistant.components.yeelight.const import (
DEFAULT_SAVE_ON_CHANGE,
DEFAULT_TRANSITION,
DOMAIN,
UPDATE_REQUEST_PROPERTIES,
YEELIGHT_HSV_TRANSACTION,
YEELIGHT_RGB_TRANSITION,
YEELIGHT_SLEEP_TRANSACTION,
@@ -1251,6 +1252,87 @@ async def test_ambilight_with_nightlight_disabled(hass: HomeAssistant) -> None:
assert state.attributes[ATTR_BRIGHTNESS] == 128
async def test_ambilight_effect(hass: HomeAssistant) -> None:
"""Test an effect on the ambilight is tracked separately from the main light."""
mocked_bulb = _mocked_bulb()
capabilities = {**CAPABILITIES, "model": "ceiling10"}
mocked_bulb.last_properties = {
**PROPERTIES,
"flowing": "0",
"bg_flowing": "0",
}
mocked_bulb.bulb_type = BulbType.WhiteTempMood
config_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_ENTRY_DATA)
config_entry.add_to_hass(hass)
with (
_patch_discovery(capabilities=capabilities),
_patch_discovery_interval(),
patch(f"{MODULE}.AsyncBulb", return_value=mocked_bulb),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
main_light_entity_id = "light.yeelight_ceiling10_0x15243f"
ambilight_entity_id = f"{main_light_entity_id}_ambilight"
mocked_bulb.async_get_properties.reset_mock()
async def _async_get_properties(properties: list[str]) -> None:
assert properties == UPDATE_REQUEST_PROPERTIES
assert mocked_bulb.async_start_flow.await_count > 0
mocked_bulb.last_properties["bg_flowing"] = "1"
mocked_bulb.async_get_properties.side_effect = _async_get_properties
await hass.services.async_call(
"light",
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ambilight_entity_id, ATTR_EFFECT: EFFECT_DISCO},
blocking=True,
)
mocked_bulb.async_start_flow.assert_awaited_once_with(
ANY, light_type=LightType.Ambient
)
mocked_bulb.async_get_properties.assert_awaited_once_with(UPDATE_REQUEST_PROPERTIES)
assert hass.states.get(main_light_entity_id).attributes.get(ATTR_EFFECT) is None
ambilight_state = hass.states.get(ambilight_entity_id)
assert ambilight_state.attributes[ATTR_EFFECT] == EFFECT_DISCO
assert ambilight_state.attributes["flowing"] is True
await hass.services.async_call(
"light",
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ambilight_entity_id, ATTR_EFFECT: EFFECT_WHATSAPP},
blocking=True,
)
assert mocked_bulb.async_get_properties.await_count == 2
assert (
hass.states.get(ambilight_entity_id).attributes[ATTR_EFFECT] == EFFECT_WHATSAPP
)
async def _async_get_stopped_properties(properties: list[str]) -> None:
assert properties == UPDATE_REQUEST_PROPERTIES
assert mocked_bulb.async_stop_flow.await_count > 0
mocked_bulb.last_properties["bg_flowing"] = "0"
mocked_bulb.async_get_properties.side_effect = _async_get_stopped_properties
await hass.services.async_call(
"light",
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ambilight_entity_id, ATTR_EFFECT: EFFECT_STOP},
blocking=True,
)
mocked_bulb.async_stop_flow.assert_awaited_once_with(light_type=LightType.Ambient)
assert mocked_bulb.async_get_properties.await_count == 3
ambilight_state = hass.states.get(ambilight_entity_id)
assert ambilight_state.attributes.get(ATTR_EFFECT) is None
assert ambilight_state.attributes["flowing"] is False
async def test_state_fails_to_update_triggers_update(hass: HomeAssistant) -> None:
"""Ensure async_get_properties is called on failed state update."""
mocked_bulb = _mocked_bulb()