|
@@ -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())
|