tuanchris 3 месяцев назад
Родитель
Сommit
152365eb9d
2 измененных файлов с 64 добавлено и 46 удалено
  1. 3 0
      modules/core/pattern_manager.py
  2. 61 46
      modules/led/hyperion_controller.py

+ 3 - 0
modules/core/pattern_manager.py

@@ -698,6 +698,9 @@ async def run_theta_rho_file(file_path, is_playlist=False):
                         if wled_was_off_for_scheduled:
                         if wled_was_off_for_scheduled:
                             logger.info("Turning LED lights back on as Still Sands period ended")
                             logger.info("Turning LED lights back on as Still Sands period ended")
                             state.led_controller.set_power(1)
                             state.led_controller.set_power(1)
+                            # CRITICAL: Give Hyperion time to fully power on before sending more commands
+                            # Without this delay, rapid-fire requests can crash Hyperion on resource-constrained Pis
+                            await asyncio.sleep(0.5)
                         state.led_controller.effect_playing(state.hyperion_playing_effect)
                         state.led_controller.effect_playing(state.hyperion_playing_effect)
 
 
                 # Dynamically determine the speed for each movement
                 # Dynamically determine the speed for each movement

+ 61 - 46
modules/led/hyperion_controller.py

@@ -38,7 +38,9 @@ class HyperionController:
                 **params
                 **params
             }
             }
 
 
-            response = requests.post(url, json=payload, timeout=2)
+            # Reduced timeout from 2s to 1s - Hyperion should respond quickly
+            # This prevents hanging when Hyperion is under load
+            response = requests.post(url, json=payload, timeout=1)
             response.raise_for_status()
             response.raise_for_status()
             result = response.json()
             result = response.json()
 
 
@@ -196,14 +198,18 @@ class HyperionController:
 
 
 def effect_loading(hyperion_controller: HyperionController) -> bool:
 def effect_loading(hyperion_controller: HyperionController) -> bool:
     """Show loading effect - Atomic swirl effect"""
     """Show loading effect - Atomic swirl effect"""
-    # Turn on Hyperion first
-    hyperion_controller.set_power(1)
-    time.sleep(0.2)  # Give Hyperion time to power on
-    # Clear priority before setting new effect
-    hyperion_controller.clear_priority()
-    time.sleep(0.1)  # Give Hyperion time to clear
-    res = hyperion_controller.set_effect("Atomic swirl")
-    return res.get('connected', False)
+    try:
+        # Turn on Hyperion first
+        hyperion_controller.set_power(1)
+        time.sleep(0.1)  # Reduced from 0.2s to minimize blocking
+        # Clear priority before setting new effect
+        hyperion_controller.clear_priority()
+        # Removed unnecessary second sleep - Hyperion processes commands quickly
+        res = hyperion_controller.set_effect("Atomic swirl")
+        return res.get('connected', False)
+    except Exception as e:
+        logger.error(f"Error in effect_loading: {e}")
+        return False
 
 
 
 
 def effect_idle(hyperion_controller: HyperionController, effect_name: str = "off") -> bool:
 def effect_idle(hyperion_controller: HyperionController, effect_name: str = "off") -> bool:
@@ -212,18 +218,22 @@ def effect_idle(hyperion_controller: HyperionController, effect_name: str = "off
     Args:
     Args:
         effect_name: Effect name to show, "off" to clear priority (default), or None for off
         effect_name: Effect name to show, "off" to clear priority (default), or None for off
     """
     """
-    # Turn on Hyperion first
-    hyperion_controller.set_power(1)
-    time.sleep(0.2)  # Give Hyperion time to power on
-    # Clear priority before setting new effect
-    hyperion_controller.clear_priority()
-    if effect_name and effect_name != "off":
-        time.sleep(0.1)  # Give Hyperion time to clear
-        res = hyperion_controller.set_effect(effect_name)
-    else:
-        # "off" or None - already cleared above, return to default state
-        res = {"connected": True}
-    return res.get('connected', False)
+    try:
+        # Clear priority first - more efficient than power cycling
+        hyperion_controller.clear_priority()
+
+        if effect_name and effect_name != "off":
+            # Turn on Hyperion and set effect
+            hyperion_controller.set_power(1)
+            time.sleep(0.05)  # Minimal delay - Hyperion is fast
+            res = hyperion_controller.set_effect(effect_name)
+        else:
+            # "off" or None - already cleared above, return to default state
+            res = {"connected": True}
+        return res.get('connected', False)
+    except Exception as e:
+        logger.error(f"Error in effect_idle: {e}")
+        return False
 
 
 
 
 def effect_connected(hyperion_controller: HyperionController) -> bool:
 def effect_connected(hyperion_controller: HyperionController) -> bool:
@@ -233,19 +243,20 @@ def effect_connected(hyperion_controller: HyperionController) -> bool:
     should explicitly set the idle effect afterwards to ensure the user's
     should explicitly set the idle effect afterwards to ensure the user's
     configured idle effect is used.
     configured idle effect is used.
     """
     """
-    # Turn on Hyperion first
-    hyperion_controller.set_power(1)
-    time.sleep(0.2)  # Give Hyperion time to power on
-    # Clear priority before setting new effect
-    hyperion_controller.clear_priority()
-    time.sleep(0.1)  # Give Hyperion time to clear
-    # Flash green twice with explicit 1 second durations
-    res = hyperion_controller.set_color(r=8, g=255, b=0, duration=1000)
-    time.sleep(1.2)  # Wait for flash to complete
-    res = hyperion_controller.set_color(r=8, g=255, b=0, duration=1000)
-    time.sleep(1.2)  # Wait for flash to complete
-    # Don't call effect_idle here - let the caller set the configured idle effect
-    return res.get('connected', False)
+    try:
+        # Turn on Hyperion and clear in one go
+        hyperion_controller.set_power(1)
+        time.sleep(0.1)  # Reduced blocking time
+        hyperion_controller.clear_priority()
+
+        # Single green flash instead of double - reduces load
+        res = hyperion_controller.set_color(r=8, g=255, b=0, duration=1000)
+        time.sleep(1.0)  # Wait for flash to complete
+        # Don't call effect_idle here - let the caller set the configured idle effect
+        return res.get('connected', False)
+    except Exception as e:
+        logger.error(f"Error in effect_connected: {e}")
+        return False
 
 
 
 
 def effect_playing(hyperion_controller: HyperionController, effect_name: str = "off") -> bool:
 def effect_playing(hyperion_controller: HyperionController, effect_name: str = "off") -> bool:
@@ -254,15 +265,19 @@ def effect_playing(hyperion_controller: HyperionController, effect_name: str = "
     Args:
     Args:
         effect_name: Effect name to show, "off" to clear priority (default), or None for off
         effect_name: Effect name to show, "off" to clear priority (default), or None for off
     """
     """
-    # Turn on Hyperion first
-    hyperion_controller.set_power(1)
-    time.sleep(0.2)  # Give Hyperion time to power on
-    # Clear priority before setting new effect
-    hyperion_controller.clear_priority()
-    if effect_name and effect_name != "off":
-        time.sleep(0.1)  # Give Hyperion time to clear
-        res = hyperion_controller.set_effect(effect_name)
-    else:
-        # "off" or None - already cleared above, show user's configured effect/color
-        res = {"connected": True}
-    return res.get('connected', False)
+    try:
+        # Clear priority first - more efficient
+        hyperion_controller.clear_priority()
+
+        if effect_name and effect_name != "off":
+            # Turn on Hyperion and set effect
+            hyperion_controller.set_power(1)
+            time.sleep(0.05)  # Minimal delay
+            res = hyperion_controller.set_effect(effect_name)
+        else:
+            # "off" or None - already cleared above, show user's configured effect/color
+            res = {"connected": True}
+        return res.get('connected', False)
+    except Exception as e:
+        logger.error(f"Error in effect_playing: {e}")
+        return False