Skip to content

Commit

Permalink
🐛 Fix effect application and state updates in SignalRGB light
Browse files Browse the repository at this point in the history
Improve reliability of effect application and state updates:

- Add pending effect tracking
- Implement effect verification with retries
- Use delayed refresh for smoother state updates
- Enhance logging for better debugging

These changes address issues with effect application
and improve the overall stability of the component.
  • Loading branch information
hyperb1iss committed Aug 14, 2024
1 parent 39b304d commit 37abcdf
Showing 1 changed file with 63 additions and 8 deletions.
71 changes: 63 additions & 8 deletions custom_components/signalrgb/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def __init__(
self._current_effect: Effect | None = None
self._is_on: bool = False
self._brightness: int = 0 # This is now 0-100
self._pending_effect: str | None = None
LOGGER.debug("SignalRGBLight initialized: %s", self.entity_id)

async def async_added_to_hass(self) -> None:
Expand Down Expand Up @@ -204,8 +205,8 @@ async def async_turn_on(self, **kwargs: Any) -> None:
LOGGER.debug("Applying effect: %s", effect)
await self._apply_effect(effect)

LOGGER.debug("Requesting coordinator refresh")
await self.coordinator.async_request_refresh()
# Schedule a delayed refresh
self.hass.async_create_task(self._delayed_refresh())

async def async_turn_off(self, **kwargs: Any) -> None:
"""Instruct the light to turn off."""
Expand All @@ -214,12 +215,10 @@ async def async_turn_off(self, **kwargs: Any) -> None:
self._is_on = False
self.async_write_ha_state()
LOGGER.debug("Light turned off, new state: %s", self._is_on)
await asyncio.sleep(0.5) # Small delay to ensure the client has time to update
LOGGER.debug("Requesting coordinator refresh after turn off")
await self.coordinator.async_request_refresh()
self.hass.async_create_task(self._delayed_refresh())

async def _apply_effect(self, effect: str) -> None:
"""Apply the specified effect."""
"""Apply the specified effect and update state immediately."""
LOGGER.debug("Applying effect: %s for %s", effect, self.entity_id)
try:
effect_obj: Effect = await self.hass.async_add_executor_job(
Expand All @@ -230,14 +229,53 @@ async def _apply_effect(self, effect: str) -> None:
self._client.apply_effect, effect_obj.id
)

# Update state immediately
self._current_effect = effect_obj
self._pending_effect = effect
self.async_write_ha_state()
LOGGER.debug("Effect applied successfully")
LOGGER.debug("Effect applied and state updated immediately: %s", effect)

# Schedule verification
self.hass.async_create_task(self._verify_effect_applied(effect_obj))
except SignalRGBException as err:
LOGGER.error("Failed to apply effect %s: %s", effect, err)
raise HomeAssistantError(f"Failed to apply effect: {err}") from err

async def _verify_effect_applied(self, expected_effect: Effect) -> None:
"""Verify that the effect was applied correctly."""
max_retries = 3
retry_delay = 1 # second

for attempt in range(max_retries):
await asyncio.sleep(retry_delay)
try:
current_effect = await self.hass.async_add_executor_job(
self._client.get_current_effect
)
if current_effect.id == expected_effect.id:
LOGGER.debug("Effect verified: %s", expected_effect.attributes.name)
self._pending_effect = None
return
LOGGER.warning("Effect not applied correctly. Retrying...")
except SignalRGBException as err:
LOGGER.error(
"Error verifying effect (attempt %d): %s", attempt + 1, err
)

LOGGER.error(
"Failed to verify effect %s after %d attempts",
expected_effect.attributes.name,
max_retries,
)
# Trigger a coordinator refresh to ensure our state is correct
await self.coordinator.async_request_refresh()

async def _delayed_refresh(self) -> None:
"""Perform a delayed refresh of the coordinator."""
await asyncio.sleep(2)
LOGGER.debug("Performing delayed refresh for %s", self.entity_id)
await self.coordinator.async_request_refresh()

async def async_update_effect_list(self) -> None:
"""Update the list of available effects."""
LOGGER.debug("Updating effect list for %s", self.entity_id)
Expand All @@ -256,9 +294,26 @@ def _handle_coordinator_update(self) -> None:
data = self.coordinator.data
if data:
LOGGER.debug("Coordinator data: %s", data)
self._current_effect = data.get("current_effect")
new_effect = data.get("current_effect")
self._is_on = data.get("is_on", False)
self._brightness = data.get("brightness", 0) # This is now 0-100

if new_effect and (
not self._current_effect or new_effect.id != self._current_effect.id
):
self._current_effect = new_effect
if (
self._pending_effect
and new_effect.attributes.name != self._pending_effect
):
LOGGER.warning(
"Applied effect doesn't match requested effect. "
"Requested: %s, Applied: %s",
self._pending_effect,
new_effect.attributes.name,
)
self._pending_effect = None

LOGGER.debug(
"Updated state - Effect: %s, Is On: %s, Brightness: %s",
self._current_effect.attributes.name
Expand Down

0 comments on commit 37abcdf

Please sign in to comment.