Prechádzať zdrojové kódy

add HA playlist config

Tuan Nguyen 11 mesiacov pred
rodič
commit
9d9af0c573
3 zmenil súbory, kde vykonal 118 pridanie a 13 odobranie
  1. 37 4
      modules/core/state.py
  2. 77 5
      modules/mqtt/handler.py
  3. 4 4
      modules/mqtt/utils.py

+ 37 - 4
modules/core/state.py

@@ -20,7 +20,7 @@ class AppState:
         self.current_theta = 0
         self.current_theta = 0
         self.current_rho = 0
         self.current_rho = 0
         self.current_playlist_index = 0
         self.current_playlist_index = 0
-        self.playlist_mode = None
+        self.playlist_mode = "loop"
         
         
         # Machine position variables
         # Machine position variables
         self.machine_x = 0.0
         self.machine_x = 0.0
@@ -36,6 +36,9 @@ class AppState:
         self.conn = None
         self.conn = None
         self.port = None
         self.port = None
         self.wled_ip = None
         self.wled_ip = None
+        self._playlist_mode = "loop"
+        self._pause_time = 0
+        self._clear_pattern = "none"
         self.load()
         self.load()
 
 
     @property
     @property
@@ -85,8 +88,10 @@ class AppState:
         # force an empty string (and not None) if we need to unset
         # force an empty string (and not None) if we need to unset
         if value == None:
         if value == None:
             value = ""
             value = ""
+            # Also clear the playlist name when playlist is cleared
+            self._current_playlist_name = None
         if self.mqtt_handler:
         if self.mqtt_handler:
-            self.mqtt_handler.update_state(playlist=value)
+            self.mqtt_handler.update_state(playlist=value, playlist_name=None)
 
 
     @property
     @property
     def current_playlist_name(self):
     def current_playlist_name(self):
@@ -98,6 +103,30 @@ class AppState:
         if self.mqtt_handler:
         if self.mqtt_handler:
             self.mqtt_handler.update_state(playlist_name=value)
             self.mqtt_handler.update_state(playlist_name=value)
 
 
+    @property
+    def playlist_mode(self):
+        return self._playlist_mode
+
+    @playlist_mode.setter
+    def playlist_mode(self, value):
+        self._playlist_mode = value
+
+    @property
+    def pause_time(self):
+        return self._pause_time
+
+    @pause_time.setter
+    def pause_time(self, value):
+        self._pause_time = value
+
+    @property
+    def clear_pattern(self):
+        return self._clear_pattern
+
+    @clear_pattern.setter
+    def clear_pattern(self, value):
+        self._clear_pattern = value
+
     def to_dict(self):
     def to_dict(self):
         """Return a dictionary representation of the state."""
         """Return a dictionary representation of the state."""
         return {
         return {
@@ -118,7 +147,9 @@ class AppState:
             "current_playlist": self._current_playlist,
             "current_playlist": self._current_playlist,
             "current_playlist_name": self._current_playlist_name,
             "current_playlist_name": self._current_playlist_name,
             "current_playlist_index": self.current_playlist_index,
             "current_playlist_index": self.current_playlist_index,
-            "playlist_mode": self.playlist_mode,
+            "playlist_mode": self._playlist_mode,
+            "pause_time": self._pause_time,
+            "clear_pattern": self._clear_pattern,
             "port": self.port,
             "port": self.port,
             "wled_ip": self.wled_ip
             "wled_ip": self.wled_ip
         }
         }
@@ -142,7 +173,9 @@ class AppState:
         self._current_playlist = data.get("current_playlist")
         self._current_playlist = data.get("current_playlist")
         self._current_playlist_name = data.get("current_playlist_name")
         self._current_playlist_name = data.get("current_playlist_name")
         self.current_playlist_index = data.get("current_playlist_index")
         self.current_playlist_index = data.get("current_playlist_index")
-        self.playlist_mode = data.get("playlist_mode")
+        self._playlist_mode = data.get("playlist_mode", "loop")
+        self._pause_time = data.get("pause_time", 0)
+        self._clear_pattern = data.get("clear_pattern", "none")
         self.port = data.get("port", None)
         self.port = data.get("port", None)
         self.wled_ip = data.get('wled_ip', None)
         self.wled_ip = data.get('wled_ip', None)
 
 

+ 77 - 5
modules/mqtt/handler.py

@@ -160,9 +160,7 @@ class MQTTHandler(BaseMQTTHandler):
             "state_topic": f"{self.speed_topic}/state",
             "state_topic": f"{self.speed_topic}/state",
             "device": base_device,
             "device": base_device,
             "icon": "mdi:speedometer",
             "icon": "mdi:speedometer",
-            "min": 50,
-            "max": 1000,
-            "step": 50
+            "mode": "box",
         }
         }
         self._publish_discovery("number", "speed", speed_config)
         self._publish_discovery("number", "speed", speed_config)
 
 
@@ -190,6 +188,48 @@ class MQTTHandler(BaseMQTTHandler):
         }
         }
         self._publish_discovery("select", "playlist", playlist_config)
         self._publish_discovery("select", "playlist", playlist_config)
 
 
+        # Playlist Run Mode Select
+        playlist_mode_config = {
+            "name": f"{self.device_name} Playlist Mode",
+            "unique_id": f"{self.device_id}_playlist_mode",
+            "command_topic": f"{self.device_id}/playlist/mode/set",
+            "state_topic": f"{self.device_id}/playlist/mode/state",
+            "options": ["single", "loop"],
+            "device": base_device,
+            "icon": "mdi:repeat",
+            "entity_category": "config"
+        }
+        self._publish_discovery("select", "playlist_mode", playlist_mode_config)
+
+        # Playlist Pause Time Number Input
+        pause_time_config = {
+            "name": f"{self.device_name} Playlist Pause Time",
+            "unique_id": f"{self.device_id}_pause_time",
+            "command_topic": f"{self.device_id}/playlist/pause_time/set",
+            "state_topic": f"{self.device_id}/playlist/pause_time/state",
+            "device": base_device,
+            "icon": "mdi:timer",
+            "entity_category": "config",
+            "mode": "box",
+            "unit_of_measurement": "seconds",
+            "min": 0,
+            "max": 86400,
+        }
+        self._publish_discovery("number", "pause_time", pause_time_config)
+
+        # Clear Pattern Select
+        clear_pattern_config = {
+            "name": f"{self.device_name} Clear Pattern",
+            "unique_id": f"{self.device_id}_clear_pattern",
+            "command_topic": f"{self.device_id}/playlist/clear_pattern/set",
+            "state_topic": f"{self.device_id}/playlist/clear_pattern/state",
+            "options": ["none", "random", "adaptive", "clear_from_in", "clear_from_out", "clear_sideway"],
+            "device": base_device,
+            "icon": "mdi:eraser",
+            "entity_category": "config"
+        }
+        self._publish_discovery("select", "clear_pattern", clear_pattern_config)
+
     def _publish_discovery(self, component: str, config_type: str, config: dict):
     def _publish_discovery(self, component: str, config_type: str, config: dict):
         """Helper method to publish HA discovery configs."""
         """Helper method to publish HA discovery configs."""
         if not self.is_enabled:
         if not self.is_enabled:
@@ -230,6 +270,7 @@ class MQTTHandler(BaseMQTTHandler):
                 current_file = current_file.split("/")[-1].split("\\")[-1]
                 current_file = current_file.split("/")[-1].split("\\")[-1]
             self.client.publish(f"{self.pattern_select_topic}/state", current_file, retain=True)
             self.client.publish(f"{self.pattern_select_topic}/state", current_file, retain=True)
         else:
         else:
+            # Clear the pattern selection
             self.client.publish(f"{self.pattern_select_topic}/state", "None", retain=True)
             self.client.publish(f"{self.pattern_select_topic}/state", "None", retain=True)
             
             
     def _publish_playlist_state(self, playlist_name=None):
     def _publish_playlist_state(self, playlist_name=None):
@@ -240,6 +281,7 @@ class MQTTHandler(BaseMQTTHandler):
         if playlist_name:
         if playlist_name:
             self.client.publish(f"{self.playlist_select_topic}/state", playlist_name, retain=True)
             self.client.publish(f"{self.playlist_select_topic}/state", playlist_name, retain=True)
         else:
         else:
+            # Clear the playlist selection
             self.client.publish(f"{self.playlist_select_topic}/state", "None", retain=True)
             self.client.publish(f"{self.playlist_select_topic}/state", "None", retain=True)
             
             
     def _publish_serial_state(self):
     def _publish_serial_state(self):
@@ -279,7 +321,10 @@ class MQTTHandler(BaseMQTTHandler):
                 (self.speed_topic, 0),
                 (self.speed_topic, 0),
                 (f"{self.device_id}/command/stop", 0),
                 (f"{self.device_id}/command/stop", 0),
                 (f"{self.device_id}/command/pause", 0),
                 (f"{self.device_id}/command/pause", 0),
-                (f"{self.device_id}/command/play", 0)
+                (f"{self.device_id}/command/play", 0),
+                (f"{self.device_id}/playlist/mode/set", 0),
+                (f"{self.device_id}/playlist/pause_time/set", 0),
+                (f"{self.device_id}/playlist/clear_pattern/set", 0),
             ])
             ])
             # Publish discovery configurations
             # Publish discovery configurations
             self.setup_ha_discovery()
             self.setup_ha_discovery()
@@ -308,6 +353,8 @@ class MQTTHandler(BaseMQTTHandler):
                     asyncio.run_coroutine_threadsafe(
                     asyncio.run_coroutine_threadsafe(
                         self.callback_registry['run_pattern'](file_path=f"{THETA_RHO_DIR}/{pattern_name}"),
                         self.callback_registry['run_pattern'](file_path=f"{THETA_RHO_DIR}/{pattern_name}"),
                         self.main_loop
                         self.main_loop
+                    ).add_done_callback(
+                        lambda _: self._publish_pattern_state(None)  # Clear pattern after execution
                     )
                     )
                     self.client.publish(f"{self.pattern_select_topic}/state", pattern_name, retain=True)
                     self.client.publish(f"{self.pattern_select_topic}/state", pattern_name, retain=True)
             elif msg.topic == self.playlist_select_topic:
             elif msg.topic == self.playlist_select_topic:
@@ -316,8 +363,15 @@ class MQTTHandler(BaseMQTTHandler):
                 if playlist_name in self.playlists:
                 if playlist_name in self.playlists:
                     # Schedule the coroutine to run in the main event loop
                     # Schedule the coroutine to run in the main event loop
                     asyncio.run_coroutine_threadsafe(
                     asyncio.run_coroutine_threadsafe(
-                        self.callback_registry['run_playlist'](playlist_name=playlist_name),
+                        self.callback_registry['run_playlist'](
+                            playlist_name=playlist_name,
+                            run_mode=self.state.playlist_mode,
+                            pause_time=self.state.pause_time,
+                            clear_pattern=self.state.clear_pattern
+                        ),
                         self.main_loop
                         self.main_loop
+                    ).add_done_callback(
+                        lambda _: self._publish_playlist_state(None)  # Clear playlist after execution
                     )
                     )
                     self.client.publish(f"{self.playlist_select_topic}/state", playlist_name, retain=True)
                     self.client.publish(f"{self.playlist_select_topic}/state", playlist_name, retain=True)
             elif msg.topic == self.speed_topic:
             elif msg.topic == self.speed_topic:
@@ -326,6 +380,9 @@ class MQTTHandler(BaseMQTTHandler):
             elif msg.topic == f"{self.device_id}/command/stop":
             elif msg.topic == f"{self.device_id}/command/stop":
                 # Handle stop command
                 # Handle stop command
                 self.callback_registry['stop']()
                 self.callback_registry['stop']()
+                # Clear both pattern and playlist selections
+                self._publish_pattern_state(None)
+                self._publish_playlist_state(None)
             elif msg.topic == f"{self.device_id}/command/pause":
             elif msg.topic == f"{self.device_id}/command/pause":
                 # Handle pause command - only if in running state
                 # Handle pause command - only if in running state
                 if bool(self.state.current_playing_file) and not self.state.pause_requested:
                 if bool(self.state.current_playing_file) and not self.state.pause_requested:
@@ -334,6 +391,21 @@ class MQTTHandler(BaseMQTTHandler):
                 # Handle play command - only if in paused state
                 # Handle play command - only if in paused state
                 if bool(self.state.current_playing_file) and self.state.pause_requested:
                 if bool(self.state.current_playing_file) and self.state.pause_requested:
                     self.callback_registry['resume']()
                     self.callback_registry['resume']()
+            elif msg.topic == f"{self.device_id}/playlist/mode/set":
+                mode = msg.payload.decode()
+                if mode in ["single", "loop"]:
+                    state.playlist_mode = mode
+                    self.client.publish(f"{self.device_id}/playlist/mode/state", mode, retain=True)
+            elif msg.topic == f"{self.device_id}/playlist/pause_time/set":
+                pause_time = float(msg.payload.decode())
+                if 0 <= pause_time <= 60:
+                    state.pause_time = pause_time
+                    self.client.publish(f"{self.device_id}/playlist/pause_time/state", pause_time, retain=True)
+            elif msg.topic == f"{self.device_id}/playlist/clear_pattern/set":
+                clear_pattern = msg.payload.decode()
+                if clear_pattern in ["none", "random", "adaptive", "clear_from_in", "clear_from_out", "clear_sideway"]:
+                    state.clear_pattern = clear_pattern
+                    self.client.publish(f"{self.device_id}/playlist/clear_pattern/state", clear_pattern, retain=True)
             else:
             else:
                 # Handle other commands
                 # Handle other commands
                 payload = json.loads(msg.payload.decode())
                 payload = json.loads(msg.payload.decode())

+ 4 - 4
modules/mqtt/utils.py

@@ -17,11 +17,11 @@ def create_mqtt_callbacks() -> Dict[str, Callable]:
 
 
     return {
     return {
         'run_pattern': lambda file_path: run_theta_rho_file(file_path),
         'run_pattern': lambda file_path: run_theta_rho_file(file_path),
-        'run_playlist': lambda playlist_name: run_playlist(
+        'run_playlist': lambda playlist_name, run_mode="loop", pause_time=0, clear_pattern=None: run_playlist(
             playlist_name,
             playlist_name,
-            run_mode="loop",  # Default to loop mode
-            pause_time=0,  # No pause between patterns
-            clear_pattern=None  # No clearing between patterns
+            run_mode=run_mode,
+            pause_time=pause_time,
+            clear_pattern=clear_pattern
         ),
         ),
         'stop': stop_actions,
         'stop': stop_actions,
         'pause': pause_execution,
         'pause': pause_execution,