feat(notifier): route TTS to tablet when TABLET_TTS_ENABLED
When TABLET_URL and VOICE_API_KEY are set, the tablet handles TTS via its ElevenLabs proxy — local speak() is skipped. Controlled by TABLET_TTS_ENABLED (default true when tablet is configured). - notifier.speak_locally() — gate used by all local speech paths - llm._maybe_speak — no-op when tablet plays the voice - modes._handle_reset — emits response event and skips local speak when tablet TTS is on; keeps spoken fallback otherwise Tablet side in smart-home-tablet repo: /api/voice/tts endpoint + VoiceOverlay audio playback (commit ba2e… pending). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,3 +54,8 @@ LUSYA_SESSION_KEY=agent:wife:voice:home
|
|||||||
# Если не настроено, просто пропускается, ассистент работает как раньше.
|
# Если не настроено, просто пропускается, ассистент работает как раньше.
|
||||||
TABLET_URL=https://tablet.digital-home.site
|
TABLET_URL=https://tablet.digital-home.site
|
||||||
VOICE_API_KEY=your_voice_api_key_here
|
VOICE_API_KEY=your_voice_api_key_here
|
||||||
|
|
||||||
|
# TABLET_TTS_ENABLED=true (по умолчанию true когда TABLET_URL/KEY заданы) —
|
||||||
|
# голос ассистента проигрывается на планшете через ElevenLabs proxy,
|
||||||
|
# локальный mpv/speak пропускается. false = говорим локально как раньше.
|
||||||
|
TABLET_TTS_ENABLED=true
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ def _post_with_retry(session, url, headers, payload):
|
|||||||
def ask_agent_stream(text: str, agent_id: str = "cosmo") -> str:
|
def ask_agent_stream(text: str, agent_id: str = "cosmo") -> str:
|
||||||
"""Отправляет запрос к OpenClaw gateway и озвучивает ответ."""
|
"""Отправляет запрос к OpenClaw gateway и озвучивает ответ."""
|
||||||
def _maybe_speak(t: str):
|
def _maybe_speak(t: str):
|
||||||
if t.strip():
|
# Если TTS на планшете — пропускаем локальный звук, планшет зачитает по response event.
|
||||||
|
if t.strip() and notifier.speak_locally():
|
||||||
speak(t, agent_id)
|
speak(t, agent_id)
|
||||||
|
|
||||||
cfg = AGENTS.get(agent_id, AGENTS["cosmo"])
|
cfg = AGENTS.get(agent_id, AGENTS["cosmo"])
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ def _handle_reset(text: str, agent_id: str) -> bool:
|
|||||||
|
|
||||||
msg = "Начинаю новую сессию."
|
msg = "Начинаю новую сессию."
|
||||||
print(f"🔄 {msg}")
|
print(f"🔄 {msg}")
|
||||||
|
# Отправляем как response event — tablet зачитает, локально говорим только если TTS на этой машине.
|
||||||
|
notifier.response(msg, agent_id)
|
||||||
|
if notifier.speak_locally():
|
||||||
speak(msg, agent_id)
|
speak(msg, agent_id)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,30 @@ from .config import log
|
|||||||
TABLET_URL = os.getenv("TABLET_URL", "").rstrip("/")
|
TABLET_URL = os.getenv("TABLET_URL", "").rstrip("/")
|
||||||
VOICE_API_KEY = os.getenv("VOICE_API_KEY", "")
|
VOICE_API_KEY = os.getenv("VOICE_API_KEY", "")
|
||||||
|
|
||||||
|
# Когда True — локальный speak() пропускается, голос идёт через планшет.
|
||||||
|
# По умолчанию включено если TABLET_URL и VOICE_API_KEY заполнены;
|
||||||
|
# явно отключить: TABLET_TTS_ENABLED=false
|
||||||
|
TABLET_TTS_ENABLED = (
|
||||||
|
bool(TABLET_URL and VOICE_API_KEY)
|
||||||
|
and os.getenv("TABLET_TTS_ENABLED", "true").lower() in ("true", "1", "yes", "on")
|
||||||
|
)
|
||||||
|
|
||||||
# Переиспользуем HTTP сессию (keep-alive) для минимума latency
|
# Переиспользуем HTTP сессию (keep-alive) для минимума latency
|
||||||
_session = requests.Session()
|
_session = requests.Session()
|
||||||
|
|
||||||
_ENABLED = bool(TABLET_URL and VOICE_API_KEY)
|
_ENABLED = bool(TABLET_URL and VOICE_API_KEY)
|
||||||
if _ENABLED:
|
if _ENABLED:
|
||||||
print(f"🔔 Notifier: планшет {TABLET_URL}")
|
tts_where = "планшет" if TABLET_TTS_ENABLED else "локально"
|
||||||
|
print(f"🔔 Notifier: события → {TABLET_URL}, TTS: {tts_where}")
|
||||||
else:
|
else:
|
||||||
print("🔕 Notifier: отключён (нет TABLET_URL или VOICE_API_KEY в .env)")
|
print("🔕 Notifier: отключён (нет TABLET_URL или VOICE_API_KEY в .env)")
|
||||||
|
|
||||||
|
|
||||||
|
def speak_locally() -> bool:
|
||||||
|
"""True если локальный speak() должен работать (TTS на этой машине)."""
|
||||||
|
return not TABLET_TTS_ENABLED
|
||||||
|
|
||||||
|
|
||||||
def _send(event: str, **payload):
|
def _send(event: str, **payload):
|
||||||
if not _ENABLED:
|
if not _ENABLED:
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user