Sfoglia il codice sorgente

propertly check light state

tuanchris 3 mesi fa
parent
commit
105e1eeac5
1 ha cambiato i file con 101 aggiunte e 35 eliminazioni
  1. 101 35
      modules/led/hyperion_controller.py

+ 101 - 35
modules/led/hyperion_controller.py

@@ -65,7 +65,7 @@ class HyperionController:
             return {"connected": False, "message": f"Error parsing Hyperion response: {str(e)}"}
 
     def check_hyperion_status(self) -> Dict:
-        """Check Hyperion connection status and component state"""
+        """Check Hyperion connection status, component state, and active priorities"""
         result = self._send_command("serverinfo")
 
         if result.get("connected"):
@@ -73,32 +73,75 @@ class HyperionController:
             info = response.get("info", {})
             components = {c["name"]: c["enabled"] for c in info.get("components", [])}
 
+            # Get active priorities information
+            priorities = info.get("priorities", [])
+            active_priority = None
+            active_effect = None
+            active_color = None
+
+            # Find the highest priority (lowest number) active source
+            if priorities:
+                # Filter for visible priorities only
+                visible = [p for p in priorities if p.get("visible", True)]
+                if visible:
+                    # Sort by priority (lowest first)
+                    visible.sort(key=lambda x: x.get("priority", 999))
+                    active_priority = visible[0].get("priority")
+
+                    # Check if it's our priority
+                    if active_priority == self.priority:
+                        component_id = visible[0].get("componentId", "")
+                        if component_id == "EFFECT":
+                            active_effect = visible[0].get("owner", "")
+                        elif component_id == "COLOR":
+                            active_color = visible[0].get("value", {}).get("RGB")
+
             return {
                 "connected": True,
                 "is_on": components.get("ALL", False),
                 "ledstream_on": components.get("LEDDEVICE", False),
                 "hostname": info.get("hostname", "unknown"),
                 "version": info.get("version", "unknown"),
-                "message": "Hyperion is ON" if components.get("ALL", False) else "Hyperion is OFF"
+                "message": "Hyperion is ON" if components.get("ALL", False) else "Hyperion is OFF",
+                "active_priority": active_priority,
+                "active_effect": active_effect,
+                "active_color": active_color,
+                "our_priority_active": active_priority == self.priority if active_priority else False
             }
 
         return result
 
-    def set_power(self, state: int) -> Dict:
+    def set_power(self, state: int, check_current: bool = True) -> Dict:
         """
         Set Hyperion power state (component control)
         Args:
             state: 0=Off, 1=On, 2=Toggle
+            check_current: If True, check current state and skip if already in desired state
         """
         if state not in [0, 1, 2]:
             return {"connected": False, "message": "Power state must be 0 (Off), 1 (On), or 2 (Toggle)"}
 
-        if state == 2:
-            # Get current state and toggle
+        # Always check current state for toggle or when check_current is enabled
+        if state == 2 or check_current:
             status = self.check_hyperion_status()
             if not status.get("connected"):
                 return status
-            state = 0 if status.get("is_on", False) else 1
+
+            current_state = status.get("is_on", False)
+
+            if state == 2:
+                # Toggle: flip the current state
+                state = 0 if current_state else 1
+            elif check_current:
+                # Check if already in desired state
+                desired_state = bool(state)
+                if current_state == desired_state:
+                    logger.debug(f"Hyperion already {'ON' if desired_state else 'OFF'}, skipping power command")
+                    return {
+                        "connected": True,
+                        "message": f"Already in desired state ({'ON' if desired_state else 'OFF'})",
+                        "skipped": True
+                    }
 
         result = self._send_command(
             "componentstate",
@@ -135,16 +178,36 @@ class HyperionController:
 
         return result
 
-    def set_effect(self, effect_name: str, args: Optional[Dict] = None, duration: int = 86400000) -> Dict:
+    def set_effect(self, effect_name: str, args: Optional[Dict] = None, duration: int = 86400000, check_current: bool = True) -> Dict:
         """
         Set Hyperion effect
         Args:
             effect_name: Name of the effect (e.g., 'Rainbow swirl', 'Warm mood blobs')
             args: Optional effect arguments
             duration: Duration in milliseconds (default = 86400000ms = 24 hours)
+            check_current: If True, check if effect is already active and skip if so
         """
-        # Turn on Hyperion first
-        self.set_power(1)
+        # Check current state if requested
+        if check_current:
+            status = self.check_hyperion_status()
+            if not status.get("connected"):
+                return status
+
+            # Check if the same effect is already active at our priority
+            if status.get("our_priority_active") and status.get("active_effect") == effect_name:
+                logger.debug(f"Effect '{effect_name}' already active at our priority, skipping")
+                return {
+                    "connected": True,
+                    "message": f"Effect '{effect_name}' already active",
+                    "skipped": True
+                }
+
+            # Ensure Hyperion is on (with state check)
+            self.set_power(1, check_current=True)
+        else:
+            # Turn on without checking
+            self.set_power(1, check_current=False)
+
         # Clear priority before setting new effect
         self.clear_priority()
 
@@ -160,15 +223,31 @@ class HyperionController:
         result = self._send_command("effect", **params)
         return result
 
-    def clear_priority(self, priority: Optional[int] = None) -> Dict:
+    def clear_priority(self, priority: Optional[int] = None, check_current: bool = True) -> Dict:
         """
         Clear a specific priority or Dune Weaver's priority
         Args:
             priority: Priority to clear (defaults to self.priority)
+            check_current: If True, check if priority is active before clearing
         """
         if priority is None:
             priority = self.priority
 
+        # Check if the priority is actually active
+        if check_current:
+            status = self.check_hyperion_status()
+            if not status.get("connected"):
+                return status
+
+            # If our priority isn't active, no need to clear
+            if priority == self.priority and not status.get("our_priority_active"):
+                logger.debug(f"Priority {priority} not active, skipping clear")
+                return {
+                    "connected": True,
+                    "message": f"Priority {priority} not active",
+                    "skipped": True
+                }
+
         result = self._send_command("clear", priority=priority)
         return result
 
@@ -199,13 +278,8 @@ class HyperionController:
 def effect_loading(hyperion_controller: HyperionController) -> bool:
     """Show loading effect - Atomic swirl effect"""
     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")
+        # Set effect with smart checking (will check power state and current effect)
+        res = hyperion_controller.set_effect("Atomic swirl", check_current=True)
         return res.get('connected', False)
     except Exception as e:
         logger.error(f"Error in effect_loading: {e}")
@@ -219,17 +293,13 @@ def effect_idle(hyperion_controller: HyperionController, effect_name: str = "off
         effect_name: Effect name to show, "off" to clear priority (default), or None for off
     """
     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)
+            # Set effect with smart checking (will check power state and current effect)
+            res = hyperion_controller.set_effect(effect_name, check_current=True)
         else:
-            # "off" or None - already cleared above, return to default state
-            res = {"connected": True}
+            # Clear priority with smart checking (only if our priority is active)
+            res = hyperion_controller.clear_priority(check_current=True)
+
         return res.get('connected', False)
     except Exception as e:
         logger.error(f"Error in effect_idle: {e}")
@@ -266,17 +336,13 @@ def effect_playing(hyperion_controller: HyperionController, effect_name: str = "
         effect_name: Effect name to show, "off" to clear priority (default), or None for off
     """
     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)
+            # Set effect with smart checking (will check power state and current effect)
+            res = hyperion_controller.set_effect(effect_name, check_current=True)
         else:
-            # "off" or None - already cleared above, show user's configured effect/color
-            res = {"connected": True}
+            # Clear priority with smart checking (only if our priority is active)
+            res = hyperion_controller.clear_priority(check_current=True)
+
         return res.get('connected', False)
     except Exception as e:
         logger.error(f"Error in effect_playing: {e}")