Commit ·
0598343
1
Parent(s): b03d17a
fix: wait for RUN_END before starting new conversation
Browse filesThe previous code started a new conversation in _tts_finished() callback,
which could happen before HA finished the pipeline. This caused:
- APIConnectionError: connection not ready
- CancelledError in audio stream
Now:
- _tts_finished() only sends AnnounceFinished, doesn't start new conversation
- _handle_run_end() is called on RUN_END event (safe point)
- New conversation only starts after HA confirms pipeline ended
reachy_mini_ha_voice/satellite.py
CHANGED
|
@@ -165,12 +165,12 @@ class VoiceSatelliteProtocol(APIServer):
|
|
| 165 |
self.play_tts()
|
| 166 |
|
| 167 |
elif event_type == VoiceAssistantEventType.VOICE_ASSISTANT_RUN_END:
|
| 168 |
-
#
|
| 169 |
-
# _tts_finished() will handle it based on whether we continue conversation
|
| 170 |
-
if not self._tts_played:
|
| 171 |
-
self._tts_finished()
|
| 172 |
self._tts_played = False
|
| 173 |
-
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
def handle_timer_event(
|
| 176 |
self,
|
|
@@ -421,6 +421,7 @@ class VoiceSatelliteProtocol(APIServer):
|
|
| 421 |
return self._tap_conversation_mode
|
| 422 |
|
| 423 |
def stop(self) -> None:
|
|
|
|
| 424 |
self.state.active_wake_words.discard(self.state.stop_word.id)
|
| 425 |
self.state.tts_player.stop()
|
| 426 |
|
|
@@ -430,7 +431,9 @@ class VoiceSatelliteProtocol(APIServer):
|
|
| 430 |
else:
|
| 431 |
_LOGGER.debug("TTS response stopped manually")
|
| 432 |
|
| 433 |
-
|
|
|
|
|
|
|
| 434 |
|
| 435 |
def play_tts(self) -> None:
|
| 436 |
if (not self._tts_url) or self._tts_played:
|
|
@@ -455,9 +458,20 @@ class VoiceSatelliteProtocol(APIServer):
|
|
| 455 |
self.state.music_player.resume_sendspin()
|
| 456 |
|
| 457 |
def _tts_finished(self) -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
self.state.active_wake_words.discard(self.state.stop_word.id)
|
| 459 |
self.send_messages([VoiceAssistantAnnounceFinished()])
|
|
|
|
| 460 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
# Check if should continue conversation
|
| 462 |
# 1. HA requested continue (continue_conversation=1 in INTENT_END)
|
| 463 |
# 2. Tap conversation mode is active (user tapped to start continuous mode)
|
|
@@ -484,9 +498,8 @@ class VoiceSatelliteProtocol(APIServer):
|
|
| 484 |
else:
|
| 485 |
# Conversation ended, clear state
|
| 486 |
self._clear_conversation()
|
| 487 |
-
self._is_streaming_audio = False
|
| 488 |
self.unduck()
|
| 489 |
-
_LOGGER.debug("
|
| 490 |
# Reachy Mini: Return to idle
|
| 491 |
self._reachy_on_idle()
|
| 492 |
|
|
|
|
| 165 |
self.play_tts()
|
| 166 |
|
| 167 |
elif event_type == VoiceAssistantEventType.VOICE_ASSISTANT_RUN_END:
|
| 168 |
+
# Pipeline run ended - this is the safe point to start a new conversation
|
|
|
|
|
|
|
|
|
|
| 169 |
self._tts_played = False
|
| 170 |
+
self._is_streaming_audio = False
|
| 171 |
+
|
| 172 |
+
# Check if should continue conversation (after RUN_END is safe)
|
| 173 |
+
self._handle_run_end()
|
| 174 |
|
| 175 |
def handle_timer_event(
|
| 176 |
self,
|
|
|
|
| 421 |
return self._tap_conversation_mode
|
| 422 |
|
| 423 |
def stop(self) -> None:
|
| 424 |
+
"""Stop current TTS playback (e.g., user said stop word)."""
|
| 425 |
self.state.active_wake_words.discard(self.state.stop_word.id)
|
| 426 |
self.state.tts_player.stop()
|
| 427 |
|
|
|
|
| 431 |
else:
|
| 432 |
_LOGGER.debug("TTS response stopped manually")
|
| 433 |
|
| 434 |
+
# Send announce finished to HA
|
| 435 |
+
self.send_messages([VoiceAssistantAnnounceFinished()])
|
| 436 |
+
# Note: RUN_END event will handle the rest
|
| 437 |
|
| 438 |
def play_tts(self) -> None:
|
| 439 |
if (not self._tts_url) or self._tts_played:
|
|
|
|
| 458 |
self.state.music_player.resume_sendspin()
|
| 459 |
|
| 460 |
def _tts_finished(self) -> None:
|
| 461 |
+
"""Called when TTS audio playback finishes.
|
| 462 |
+
|
| 463 |
+
Note: This is called from the audio player callback, NOT from HA events.
|
| 464 |
+
We should NOT start a new conversation here - wait for RUN_END event.
|
| 465 |
+
"""
|
| 466 |
self.state.active_wake_words.discard(self.state.stop_word.id)
|
| 467 |
self.send_messages([VoiceAssistantAnnounceFinished()])
|
| 468 |
+
_LOGGER.debug("TTS playback finished, waiting for RUN_END event")
|
| 469 |
|
| 470 |
+
def _handle_run_end(self) -> None:
|
| 471 |
+
"""Handle pipeline RUN_END event - safe point to continue conversation.
|
| 472 |
+
|
| 473 |
+
This is called after HA has fully completed the pipeline run.
|
| 474 |
+
"""
|
| 475 |
# Check if should continue conversation
|
| 476 |
# 1. HA requested continue (continue_conversation=1 in INTENT_END)
|
| 477 |
# 2. Tap conversation mode is active (user tapped to start continuous mode)
|
|
|
|
| 498 |
else:
|
| 499 |
# Conversation ended, clear state
|
| 500 |
self._clear_conversation()
|
|
|
|
| 501 |
self.unduck()
|
| 502 |
+
_LOGGER.debug("Pipeline ended, conversation finished")
|
| 503 |
# Reachy Mini: Return to idle
|
| 504 |
self._reachy_on_idle()
|
| 505 |
|