idle_timeout_manager.py 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. """
  2. Idle LED Timeout Manager
  3. Handles automatic LED turn-off after a period of inactivity.
  4. """
  5. import asyncio
  6. import logging
  7. from datetime import datetime
  8. from typing import Optional
  9. logger = logging.getLogger(__name__)
  10. class IdleTimeoutManager:
  11. """
  12. Manages idle timeout for LED effects.
  13. When idle effect is played, starts a timer. When timer expires,
  14. checks if table is still idle and turns off LEDs if so.
  15. """
  16. def __init__(self):
  17. self._timeout_task: Optional[asyncio.Task] = None
  18. self._last_idle_time: Optional[datetime] = None
  19. def start_idle_timeout(self, timeout_minutes: float, state, check_idle_callback):
  20. """
  21. Start or restart the idle timeout timer.
  22. Args:
  23. timeout_minutes: Minutes to wait before turning off LEDs
  24. state: Application state object
  25. check_idle_callback: Async callback to check if table is still idle
  26. """
  27. # Cancel any existing timeout
  28. self.cancel_timeout()
  29. if timeout_minutes <= 0:
  30. logger.debug("Idle timeout disabled (timeout <= 0)")
  31. return
  32. # Record when idle effect was started
  33. self._last_idle_time = datetime.now()
  34. logger.info(f"Starting idle LED timeout: {timeout_minutes} minutes")
  35. # Create background task to handle timeout
  36. self._timeout_task = asyncio.create_task(
  37. self._timeout_handler(timeout_minutes, state, check_idle_callback)
  38. )
  39. async def _timeout_handler(self, timeout_minutes: float, state, check_idle_callback):
  40. """
  41. Background task that waits for timeout and turns off LEDs if still idle.
  42. """
  43. try:
  44. # Wait for the specified timeout
  45. timeout_seconds = timeout_minutes * 60
  46. await asyncio.sleep(timeout_seconds)
  47. # Check if we should turn off the LEDs
  48. logger.debug("Idle timeout expired, checking table state...")
  49. # Check if table is still idle (not playing anything)
  50. is_idle = await check_idle_callback()
  51. if is_idle:
  52. logger.info("Table is still idle after timeout - turning off LEDs")
  53. if state.led_controller:
  54. try:
  55. state.led_controller.set_power(0) # Turn off LEDs
  56. logger.info("LEDs turned off successfully")
  57. except Exception as e:
  58. logger.error(f"Failed to turn off LEDs: {e}")
  59. else:
  60. logger.warning("LED controller not configured")
  61. else:
  62. logger.debug("Table is not idle - skipping LED turn-off")
  63. except asyncio.CancelledError:
  64. logger.debug("Idle timeout cancelled")
  65. except Exception as e:
  66. logger.error(f"Error in idle timeout handler: {e}")
  67. def cancel_timeout(self):
  68. """Cancel any running timeout task."""
  69. if self._timeout_task and not self._timeout_task.done():
  70. logger.debug("Cancelling existing idle timeout")
  71. self._timeout_task.cancel()
  72. self._timeout_task = None
  73. def is_timeout_active(self) -> bool:
  74. """Check if a timeout is currently active."""
  75. return self._timeout_task is not None and not self._timeout_task.done()
  76. # Singleton instance
  77. idle_timeout_manager = IdleTimeoutManager()