1
0

led_interface.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. """
  2. Unified LED interface for different LED control systems
  3. Provides a common abstraction layer for pattern manager integration.
  4. """
  5. import asyncio
  6. from typing import Optional, Literal
  7. from modules.led.led_controller import LEDController, effect_loading as wled_loading, effect_idle as wled_idle, effect_connected as wled_connected, effect_playing as wled_playing
  8. # Try to import DW LED controller - it requires RPi-specific dependencies
  9. try:
  10. from modules.led.dw_led_controller import DWLEDController, effect_loading as dw_led_loading, effect_idle as dw_led_idle, effect_connected as dw_led_connected, effect_playing as dw_led_playing
  11. DW_LEDS_AVAILABLE = True
  12. except ImportError:
  13. # Running on non-RPi platform - DW LEDs not available
  14. DWLEDController = None
  15. dw_led_loading = None
  16. dw_led_idle = None
  17. dw_led_connected = None
  18. dw_led_playing = None
  19. DW_LEDS_AVAILABLE = False
  20. LEDProviderType = Literal["wled", "dw_leds", "none"]
  21. class LEDInterface:
  22. """
  23. Unified interface for LED control that works with multiple backends.
  24. Automatically delegates to the appropriate controller based on configuration.
  25. """
  26. def __init__(self, provider: LEDProviderType = "none", ip_address: Optional[str] = None,
  27. num_leds: Optional[int] = None, gpio_pin: Optional[int] = None, pixel_order: Optional[str] = None,
  28. brightness: Optional[float] = None, speed: Optional[int] = None, intensity: Optional[int] = None):
  29. self.provider = provider
  30. self._controller = None
  31. if provider == "wled" and ip_address:
  32. self._controller = LEDController(ip_address)
  33. elif provider == "dw_leds":
  34. if not DW_LEDS_AVAILABLE:
  35. raise ImportError("DW LED controller requires Raspberry Pi GPIO libraries. Install with: pip install -r requirements.txt")
  36. # DW LEDs uses local GPIO, no IP needed
  37. num_leds = num_leds or 60
  38. gpio_pin = gpio_pin or 12
  39. pixel_order = pixel_order or "GRB"
  40. brightness = brightness if brightness is not None else 0.35
  41. speed = speed if speed is not None else 128
  42. intensity = intensity if intensity is not None else 128
  43. self._controller = DWLEDController(num_leds, gpio_pin, brightness, pixel_order=pixel_order, speed=speed, intensity=intensity)
  44. @property
  45. def is_configured(self) -> bool:
  46. """Check if LED controller is configured"""
  47. return self._controller is not None
  48. def update_config(self, provider: LEDProviderType, ip_address: Optional[str] = None,
  49. num_leds: Optional[int] = None, gpio_pin: Optional[int] = None, pixel_order: Optional[str] = None,
  50. brightness: Optional[float] = None, speed: Optional[int] = None, intensity: Optional[int] = None):
  51. """Update LED provider configuration"""
  52. self.provider = provider
  53. # Stop existing controller if switching providers
  54. if self._controller and hasattr(self._controller, 'stop'):
  55. try:
  56. self._controller.stop()
  57. except:
  58. pass
  59. if provider == "wled" and ip_address:
  60. self._controller = LEDController(ip_address)
  61. elif provider == "dw_leds":
  62. if not DW_LEDS_AVAILABLE:
  63. raise ImportError("DW LED controller requires Raspberry Pi GPIO libraries. Install with: pip install -r requirements.txt")
  64. num_leds = num_leds or 60
  65. gpio_pin = gpio_pin or 12
  66. pixel_order = pixel_order or "GRB"
  67. brightness = brightness if brightness is not None else 0.35
  68. speed = speed if speed is not None else 128
  69. intensity = intensity if intensity is not None else 128
  70. self._controller = DWLEDController(num_leds, gpio_pin, brightness, pixel_order=pixel_order, speed=speed, intensity=intensity)
  71. else:
  72. self._controller = None
  73. def effect_loading(self) -> bool:
  74. """Show loading effect"""
  75. if not self.is_configured:
  76. return False
  77. if self.provider == "wled":
  78. return wled_loading(self._controller)
  79. elif self.provider == "dw_leds":
  80. return dw_led_loading(self._controller)
  81. return False
  82. def effect_idle(self, effect_name: Optional[str] = None) -> bool:
  83. """Show idle effect"""
  84. if not self.is_configured:
  85. return False
  86. if self.provider == "wled":
  87. return wled_idle(self._controller)
  88. elif self.provider == "dw_leds":
  89. return dw_led_idle(self._controller, effect_name)
  90. return False
  91. def effect_connected(self) -> bool:
  92. """Show connected effect"""
  93. if not self.is_configured:
  94. return False
  95. if self.provider == "wled":
  96. return wled_connected(self._controller)
  97. elif self.provider == "dw_leds":
  98. return dw_led_connected(self._controller)
  99. return False
  100. def effect_playing(self, effect_name: Optional[str] = None) -> bool:
  101. """Show playing effect"""
  102. if not self.is_configured:
  103. return False
  104. if self.provider == "wled":
  105. return wled_playing(self._controller)
  106. elif self.provider == "dw_leds":
  107. return dw_led_playing(self._controller, effect_name)
  108. return False
  109. def set_power(self, state: int) -> dict:
  110. """Set power state (0=Off, 1=On, 2=Toggle)"""
  111. if not self.is_configured:
  112. return {"connected": False, "message": "No LED controller configured"}
  113. return self._controller.set_power(state)
  114. def check_status(self) -> dict:
  115. """Check controller status"""
  116. if not self.is_configured:
  117. return {"connected": False, "message": "No LED controller configured"}
  118. if self.provider == "wled":
  119. return self._controller.check_wled_status()
  120. elif self.provider == "dw_leds":
  121. return self._controller.check_status()
  122. return {"connected": False, "message": "Unknown provider"}
  123. def get_controller(self):
  124. """Get the underlying controller instance (for advanced usage)"""
  125. return self._controller
  126. # Async versions of methods for non-blocking calls from async context
  127. # These use asyncio.to_thread() to avoid blocking the event loop
  128. async def effect_loading_async(self) -> bool:
  129. """Show loading effect (non-blocking)"""
  130. return await asyncio.to_thread(self.effect_loading)
  131. async def effect_idle_async(self, effect_name: Optional[str] = None) -> bool:
  132. """Show idle effect (non-blocking)"""
  133. return await asyncio.to_thread(self.effect_idle, effect_name)
  134. async def effect_connected_async(self) -> bool:
  135. """Show connected effect (non-blocking)"""
  136. return await asyncio.to_thread(self.effect_connected)
  137. async def effect_playing_async(self, effect_name: Optional[str] = None) -> bool:
  138. """Show playing effect (non-blocking)"""
  139. return await asyncio.to_thread(self.effect_playing, effect_name)
  140. async def set_power_async(self, state: int) -> dict:
  141. """Set power state (non-blocking)"""
  142. return await asyncio.to_thread(self.set_power, state)
  143. async def check_status_async(self) -> dict:
  144. """Check controller status (non-blocking)"""
  145. return await asyncio.to_thread(self.check_status)