feat(voice): Wake-Word per Sprachbefehl ausschalten

"Computer, Wake-Word aus" / "mach das Ohr aus" / "hör auf zuzuhören" / "geh
schlafen" → die App stoppt den Wake-Word-Listener KOMPLETT (Mikro frei, echte
Ruhe). Wieder-An nur über den Ohr-Button (bewusst, weil dann taub — Voice-
Wieder-An ist physikalisch unmöglich, wenn nichts mehr hört).

Deterministischer Detektor _user_wants_wake_off im Brain (kein LLM-Call nötig,
still). Signal fließt als wake_off durch ChatOut → Bridge → App-Payload; die App
löst denselben Pfad wie toggleWakeWord-off aus (cancelRecording +
wakeWordService.stop() + setWakeWordActive(false)). Detektor gegen 13 Positiv-
+ 9 Negativ-Fällen verifiziert (fängt nicht 'Licht aus' / 'Konversation Ende').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 20:27:48 +02:00
co-authored by Claude Opus 4.8
parent c4e658446d
commit 93401d42d1
4 changed files with 66 additions and 2 deletions
+7 -1
View File
@@ -1622,6 +1622,9 @@ class ARIABridge:
# die Projekt-Queue und leitet die naechste Eingabe als Antwort auf
# DIESE Rueckfrage weiter, statt sie als neuen Auftrag anzustellen.
"awaiting_reply": bool(payload.get("awaiting_reply", False)) if isinstance(payload, dict) else False,
# User hat "Wake-Word aus" gesagt → App stoppt den Listener komplett
# (Mikro frei). Wieder-An nur ueber den App-Button.
"wake_off": bool(payload.get("wake_off", False)) if isinstance(payload, dict) else False,
},
"timestamp": int(asyncio.get_event_loop().time() * 1000),
})
@@ -2017,6 +2020,8 @@ class ARIABridge:
# Stellt ARIA eine blockierende Rueckfrage? Dann pausiert die App die
# Projekt-Queue und leitet die naechste Eingabe als Antwort weiter.
awaiting_reply = bool(data.get("awaiting_reply", False))
# User hat per Sprache "Wake-Word aus" gesagt → App stoppt den Listener.
wake_off = bool(data.get("wake_off", False))
# Side-Channel-Events VOR der Chat-Bubble broadcasten (z.B. skill_created)
# damit sie in der UI vor der Reply auftauchen
@@ -2108,7 +2113,8 @@ class ARIABridge:
"answeredBy": answered_by,
"speak": speak,
"converse": converse,
"awaiting_reply": awaiting_reply})
"awaiting_reply": awaiting_reply,
"wake_off": wake_off})
except Exception:
logger.exception("[brain] _process_core_response Fehler")
await self._emit_activity("idle", "", project_id=project_id)