10 sekunden warten nach neuer chat, instruktionen verbessert.

This commit is contained in:
duffyduck 2025-12-27 02:59:34 +01:00
parent 90791b9ff6
commit 38e653361e
3 changed files with 116 additions and 16 deletions

View File

@ -166,8 +166,9 @@ Claude verwendet diese Befehle in eckigen Klammern:
- **Echte Autonomie** - Claude entscheidet selbst was ihn interessiert
- **Paralelle Konversation** - Erkunden UND quatschen gleichzeitig
- **Sprachausgabe** - Claude redet mit dir (TTS)
- **Spracheingabe** - Du redest mit Claude (STT)
- **Spracheingabe** - Du redest mit Claude (STT, 5s Stille = fertig)
- **Mute/Unmute** - Mikrofon per Tastendruck stummschalten
- **Smart Recording** - Heartbeat pausiert automatisch während du sprichst
- **Hinderniserkennung** - Ultraschall & IMU
- **Touch-Display** - Notfall-Stopp & Status
- **Termux Support** - Läuft auch auf Android!

View File

@ -245,6 +245,31 @@ python chat_audio_bridge.py -c config.local.yaml
- Mit **N** startest du einen neuen Chat und die Instruktionen werden erneut gesendet
- Bilder werden nur hochgeladen wenn sie sich geändert haben (spart Limit!)
### 2.7 Spracheingabe (STT) - Wie es funktioniert
Die Spracheingabe sammelt deine Worte intelligent:
1. **Aufnahme startet** sobald du sprichst
2. **Heartbeat pausiert** automatisch während du redest
3. **Mehrere Sätze** werden gesammelt und zusammengefügt
4. **Nach 5 Sekunden Stille** gilt die Eingabe als komplett
5. **Mit dem nächsten TICK** wird alles an Claude gesendet
**Beispiel-Ablauf:**
```
Du: "Hallo Claude..." → 🎤 Stefan spricht...
Du: "...kannst du mal..." → → Hallo Claude
Du: "...nach links schauen?" → → kannst du mal
→ → nach links schauen?
[5 Sekunden Stille] → ✓ Stefan (komplett): Hallo Claude kannst du mal nach links schauen?
[TICK wird gesendet] → Claude bekommt die komplette Nachricht
```
**Warum 5 Sekunden?**
- Gibt dir Zeit zum Nachdenken zwischen Sätzen
- Verhindert dass halbe Sätze gesendet werden
- Claude bekommt immer den kompletten Gedanken
---
## Teil 3: Hardware zusammenbauen

View File

@ -120,11 +120,19 @@ class ClaudesEyesAudioBridge:
self._sending = threading.Event()
self._sending.set() # Anfangs nicht am Senden (set = frei)
# Recording-Flag: Wenn gesetzt, wird gerade aufgenommen
# Heartbeat pausiert während Aufnahme aktiv ist
self._recording = threading.Event()
self._recording.clear() # Anfangs nicht am Aufnehmen
# Mute-Flag: Wenn True, ignoriert STT alle Eingaben
# Startet gemutet um ungewollte Aufnahmen zu vermeiden
self._muted = True
self._mute_lock = threading.Lock()
# Silence-Timeout: Wie lange Stille bevor Aufnahme als fertig gilt
self._silence_timeout = 5.0 # Sekunden
def _load_config(self, config_path: str) -> dict:
"""Lädt die Konfiguration"""
path = Path(config_path)
@ -428,7 +436,8 @@ Die Befehle werden aus der TTS-Ausgabe rausgefiltert.
- Du musst nicht bei jedem TICK fahren - manchmal reicht auch schauen und kommentieren
## WICHTIG: Bestätige mit [READY]
Wenn du diese Instruktionen verstanden hast, antworte mit **[READY]** am Ende deiner Nachricht.
Wenn du diese Instruktionen verstanden hast, antworte mit dem Tag `[READY]` (exakt so, in eckigen Klammern!) am Ende deiner Nachricht.
**Das Format muss EXAKT `[READY]` sein** - nicht fett, nicht anders formatiert, sondern genau so: [READY]
Erst dann starten die automatischen TICKs mit Bildern!"""
console.print("[cyan]→ Sende Instruktionen an Claude...[/cyan]")
@ -500,6 +509,16 @@ Erst dann starten die automatischen TICKs mit Bildern!"""
if not self.running:
break
# Warte bis Recording fertig ist (5s Stille)
if self._recording.is_set():
logger.debug("Stefan spricht noch, warte auf Stille...")
while self.running and self._recording.is_set():
time.sleep(0.5)
logger.debug("Stefan fertig, fahre fort")
if not self.running:
break
# Zufällige Pause nach Claudes Antwort (natürlicheres Tempo)
pause = random.uniform(min_pause, max_pause)
time.sleep(pause)
@ -511,7 +530,7 @@ Erst dann starten die automatischen TICKs mit Bildern!"""
stefan_text = self._get_and_clear_stefan_buffer()
# Warte bis vorheriges Senden fertig ist
self._sending.wait()
self._sending.wait(timeout=30) # Max 30s warten
# Nächsten TICK senden (mit oder ohne Bild)
with self._lock:
@ -622,40 +641,67 @@ Erst dann starten die automatischen TICKs mit Bildern!"""
"""
Hört auf Stefan und sammelt seine Worte im Buffer.
Wenn Claude tippt Buffer sammeln
Wenn Claude fertig Buffer wird mit nächstem TICK gesendet
Wenn gemutet Ignoriert alle Eingaben
Ablauf:
1. Warte auf erste Spracheingabe
2. Signalisiere Recording aktiv Heartbeat pausiert
3. Sammle weitere Eingaben bis 5 Sekunden Stille
4. Recording fertig Buffer wird mit nächstem TICK gesendet
So wird Claude nicht unterbrochen und bekommt alles gesammelt.
Wenn gemutet Ignoriert alle Eingaben
"""
if not self.stt:
logger.warning("STT nicht verfügbar")
return
logger.info("STT-Loop gestartet (mit Buffer)")
logger.info(f"STT-Loop gestartet (5s Stille = fertig)")
# Temporärer Buffer für aktuelle Aufnahme-Session
current_session_texts = []
last_speech_time = 0
while self.running:
try:
# Wenn gemutet, kurz warten und überspringen
if self.is_muted():
# Falls wir mitten in einer Aufnahme waren, diese beenden
if self._recording.is_set():
self._finalize_recording(current_session_texts)
current_session_texts = []
time.sleep(0.5)
continue
# Warte auf Sprache (mit Timeout)
result = self.stt.listen_once(timeout=2)
# Warte auf Sprache (kurzer Timeout für schnelle Reaktion)
result = self.stt.listen_once(timeout=1)
# Nochmal prüfen nach dem Hören (falls zwischendurch gemutet wurde)
if self.is_muted():
continue
if result and result.text and len(result.text) > 2:
# In Buffer speichern (thread-safe)
with self._stefan_buffer_lock:
self._stefan_buffer.append(result.text)
self.stats.stefan_inputs += 1
# Sprache erkannt!
current_session_texts.append(result.text)
last_speech_time = time.time()
self.stats.stefan_inputs += 1
console.print(f"\n[bold green]Stefan (gebuffert):[/bold green] {result.text}")
logger.debug(f"Stefan-Buffer: {len(self._stefan_buffer)} Einträge")
# Signalisiere dass Recording aktiv ist
if not self._recording.is_set():
self._recording.set()
console.print(f"\n[bold green]🎤 Stefan spricht...[/bold green]")
logger.debug("Recording gestartet")
console.print(f"[green] → {result.text}[/green]")
logger.debug(f"Stefan-Session: {len(current_session_texts)} Teile")
else:
# Keine Sprache erkannt - prüfe auf Stille-Timeout
if self._recording.is_set() and last_speech_time > 0:
silence_duration = time.time() - last_speech_time
if silence_duration >= self._silence_timeout:
# 5 Sekunden Stille - Aufnahme beenden
self._finalize_recording(current_session_texts)
current_session_texts = []
last_speech_time = 0
except Exception as e:
# Timeout ist normal
@ -663,6 +709,30 @@ Erst dann starten die automatischen TICKs mit Bildern!"""
logger.error(f"STT-Loop-Fehler: {e}")
self.stats.errors += 1
def _finalize_recording(self, texts: list):
"""
Beendet eine Aufnahme-Session und speichert im Buffer.
Args:
texts: Liste der erkannten Texte in dieser Session
"""
if not texts:
self._recording.clear()
return
# Alle Texte zusammenfügen
full_text = " ".join(texts)
# In Haupt-Buffer speichern
with self._stefan_buffer_lock:
self._stefan_buffer.append(full_text)
console.print(f"\n[bold green]✓ Stefan (komplett):[/bold green] {full_text}")
logger.info(f"Recording beendet: {len(texts)} Teile → {len(full_text)} Zeichen")
# Recording-Flag zurücksetzen → Heartbeat kann weitermachen
self._recording.clear()
def _keyboard_loop(self):
"""
Hört auf Tastatureingaben für Mute-Toggle und andere Befehle.
@ -730,6 +800,10 @@ Erst dann starten die automatischen TICKs mit Bildern!"""
# Neue URL in config.yaml speichern
self._save_chat_url_to_config(new_url)
# Warte bis der neue Chat vollständig geladen ist
console.print("[dim]Warte 10s bis Chat geladen...[/dim]")
time.sleep(10)
console.print("[cyan]Sende Instruktionen mit Referenz zum alten Chat...[/cyan]")
# Instruktionen erneut senden (mit Referenz zum alten Chat)