02-ago: [proyecto] vigilancia - anuncio atemorizante en teles Google: voz grave (Alvaro pitch-15Hz), volumen 30, mensaje configurable, apagado automatico al terminar
This commit is contained in:
@@ -42,7 +42,15 @@ DISCORD_IDS=
|
|||||||
# Teles Google (Chromecast/Google TV) que hablan el aviso.
|
# Teles Google (Chromecast/Google TV) que hablan el aviso.
|
||||||
# GOOGLE_TV_ACTIVO=1/0 (siempre por defecto activo).
|
# GOOGLE_TV_ACTIVO=1/0 (siempre por defecto activo).
|
||||||
# GOOGLE_TV_NOMBRES: nombres de las teles separados por coma (vacio = todas las detectadas).
|
# GOOGLE_TV_NOMBRES: nombres de las teles separados por coma (vacio = todas las detectadas).
|
||||||
|
# GOOGLE_TV_VOLUMEN: volumen al que se enciende (0-100). GOOGLE_TV_APAGAR=1 apaga tras el anuncio.
|
||||||
|
# VOZ_EDGE/VOZ_PITCH/VOZ_RATE: voz (Alvaro=grave), tono y velocidad.
|
||||||
GOOGLE_TV_ACTIVO=1
|
GOOGLE_TV_ACTIVO=1
|
||||||
GOOGLE_TV_NOMBRES=
|
GOOGLE_TV_NOMBRES=
|
||||||
VOZ_EDGE=es-ES-ElviraNeural
|
GOOGLE_TV_VOLUMEN=30
|
||||||
|
GOOGLE_TV_APAGAR=1
|
||||||
|
VOZ_EDGE=es-ES-AlvaroNeural
|
||||||
|
VOZ_PITCH=-15Hz
|
||||||
|
VOZ_RATE=-10%
|
||||||
GOOGLE_TV_PUERTO=8500
|
GOOGLE_TV_PUERTO=8500
|
||||||
|
VOZ_MENSAJE_ALERTA=Por favor, os avisamos de que no podéis cruzar este límite. Tenéis que ir a vuestro cuarto.
|
||||||
|
VOZ_MENSAJE_CONOCIDO={nombres}, ¿qué estás haciendo? No entres ahí.
|
||||||
|
|||||||
@@ -46,5 +46,18 @@ GOOGLE_TV_ACTIVO = os.getenv("GOOGLE_TV_ACTIVO", "1") == "1"
|
|||||||
GOOGLE_TV_NOMBRES = {
|
GOOGLE_TV_NOMBRES = {
|
||||||
n.strip() for n in os.getenv("GOOGLE_TV_NOMBRES", "").split(",") if n.strip()
|
n.strip() for n in os.getenv("GOOGLE_TV_NOMBRES", "").split(",") if n.strip()
|
||||||
}
|
}
|
||||||
VOZ_EDGE = os.getenv("VOZ_EDGE", "es-ES-ElviraNeural")
|
GOOGLE_TV_VOLUMEN = int(os.getenv("GOOGLE_TV_VOLUMEN", "30"))
|
||||||
|
GOOGLE_TV_APAGAR = os.getenv("GOOGLE_TV_APAGAR", "1") == "1"
|
||||||
|
VOZ_EDGE = os.getenv("VOZ_EDGE", "es-ES-AlvaroNeural")
|
||||||
|
VOZ_PITCH = os.getenv("VOZ_PITCH", "-15Hz")
|
||||||
|
VOZ_RATE = os.getenv("VOZ_RATE", "-10%")
|
||||||
GOOGLE_TV_PUERTO = int(os.getenv("GOOGLE_TV_PUERTO", "8500"))
|
GOOGLE_TV_PUERTO = int(os.getenv("GOOGLE_TV_PUERTO", "8500"))
|
||||||
|
VOZ_MENSAJE_ALERTA = os.getenv(
|
||||||
|
"VOZ_MENSAJE_ALERTA",
|
||||||
|
"Por favor, os avisamos de que no podéis cruzar este límite. "
|
||||||
|
"Tenéis que ir a vuestro cuarto.",
|
||||||
|
)
|
||||||
|
VOZ_MENSAJE_CONOCIDO = os.getenv(
|
||||||
|
"VOZ_MENSAJE_CONOCIDO",
|
||||||
|
"{nombres}, ¿qué estás haciendo? No entres ahí.",
|
||||||
|
)
|
||||||
|
|||||||
@@ -39,14 +39,16 @@ def notificar_discord(resultados, cam_nombre):
|
|||||||
def texto_voz(resultados, cam_nombre):
|
def texto_voz(resultados, cam_nombre):
|
||||||
conocidas = {r["nombre"] for r in resultados if "nombre" in r}
|
conocidas = {r["nombre"] for r in resultados if "nombre" in r}
|
||||||
if conocidas:
|
if conocidas:
|
||||||
return f"{', '.join(sorted(conocidas))}, ¿qué estás haciendo? No entres ahí."
|
return config.VOZ_MENSAJE_CONOCIDO.format(
|
||||||
return f"Presencia detectada en {cam_nombre}."
|
nombres=", ".join(sorted(conocidas)), camara=cam_nombre
|
||||||
|
)
|
||||||
|
return config.VOZ_MENSAJE_ALERTA.format(camara=cam_nombre)
|
||||||
|
|
||||||
|
|
||||||
def hablar_en_teles(mensaje):
|
def hablar_en_teles(mensaje):
|
||||||
try:
|
try:
|
||||||
from voz import reproducir_en_teles
|
from voz import anuncio_en_teles
|
||||||
reproducir_en_teles(mensaje)
|
anuncio_en_teles(mensaje)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[TV] Error: {e}")
|
print(f"[TV] Error: {e}")
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import asyncio
|
|||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||||
|
|
||||||
import config
|
import config
|
||||||
@@ -19,14 +20,19 @@ def _ip_lan():
|
|||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
|
|
||||||
def sintetizar(texto, nombre="alerta.mp3"):
|
def sintetizar(texto, nombre="anuncio.mp3"):
|
||||||
os.makedirs(MEDIA_DIR, exist_ok=True)
|
os.makedirs(MEDIA_DIR, exist_ok=True)
|
||||||
ruta = os.path.join(MEDIA_DIR, nombre)
|
ruta = os.path.join(MEDIA_DIR, nombre)
|
||||||
try:
|
try:
|
||||||
import edge_tts
|
import edge_tts
|
||||||
|
|
||||||
async def _gen():
|
async def _gen():
|
||||||
comunicador = edge_tts.Communicate(texto, config.VOZ_EDGE)
|
comunicador = edge_tts.Communicate(
|
||||||
|
texto,
|
||||||
|
config.VOZ_EDGE,
|
||||||
|
rate=config.VOZ_RATE,
|
||||||
|
pitch=config.VOZ_PITCH,
|
||||||
|
)
|
||||||
await comunicador.save(ruta)
|
await comunicador.save(ruta)
|
||||||
|
|
||||||
asyncio.run(_gen())
|
asyncio.run(_gen())
|
||||||
@@ -55,17 +61,51 @@ def _arrancar_servidor():
|
|||||||
print(f"[Voz] No se pudo arrancar el servidor HTTP: {e}")
|
print(f"[Voz] No se pudo arrancar el servidor HTTP: {e}")
|
||||||
|
|
||||||
|
|
||||||
def reproducir_en_teles(texto):
|
def _descubrir():
|
||||||
if not config.GOOGLE_TV_ACTIVO:
|
import pychromecast
|
||||||
return
|
|
||||||
ruta = sintetizar(texto)
|
chromecasts, browser = pychromecast.get_chromecasts(timeout=8)
|
||||||
|
try:
|
||||||
|
if config.GOOGLE_TV_NOMBRES:
|
||||||
|
return [c for c in chromecasts
|
||||||
|
if c.cast_info.friendly_name in config.GOOGLE_TV_NOMBRES]
|
||||||
|
return chromecasts
|
||||||
|
finally:
|
||||||
|
pychromecast.discovery.stop_discovery(browser)
|
||||||
|
|
||||||
|
|
||||||
|
def _url_audio(texto, nombre="anuncio.mp3"):
|
||||||
|
ruta = sintetizar(texto, nombre=nombre)
|
||||||
if ruta is None:
|
if ruta is None:
|
||||||
return
|
return None
|
||||||
_arrancar_servidor()
|
_arrancar_servidor()
|
||||||
if _server is None:
|
if _server is None:
|
||||||
|
return None
|
||||||
|
return f"http://{_ip_lan()}:{config.GOOGLE_TV_PUERTO}/{os.path.basename(ruta)}"
|
||||||
|
|
||||||
|
|
||||||
|
def _esperar_fin(cast, timeout=90):
|
||||||
|
mc = cast.media_controller
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < timeout:
|
||||||
|
try:
|
||||||
|
st = mc.status
|
||||||
|
if st.player_state == "IDLE":
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(1)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def anuncio_en_teles(texto):
|
||||||
|
"""Enciende la tele con el volumen configurado, reproduce el mensaje y la apaga."""
|
||||||
|
if not config.GOOGLE_TV_ACTIVO:
|
||||||
|
return
|
||||||
|
url = _url_audio(texto)
|
||||||
|
if url is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
url = f"http://{_ip_lan()}:{config.GOOGLE_TV_PUERTO}/{os.path.basename(ruta)}"
|
|
||||||
try:
|
try:
|
||||||
import pychromecast
|
import pychromecast
|
||||||
|
|
||||||
@@ -84,12 +124,23 @@ def reproducir_en_teles(texto):
|
|||||||
for c in objetivos:
|
for c in objetivos:
|
||||||
try:
|
try:
|
||||||
c.wait(timeout=10)
|
c.wait(timeout=10)
|
||||||
|
c.set_volume(config.GOOGLE_TV_VOLUMEN / 100.0)
|
||||||
c.media_controller.play_media(url, "audio/mpeg", title=texto[:80])
|
c.media_controller.play_media(url, "audio/mpeg", title=texto[:80])
|
||||||
c.media_controller.block_until_active(timeout=10)
|
c.media_controller.block_until_active(timeout=10)
|
||||||
print(f"[Voz] Reproduciendo en {c.cast_info.friendly_name}")
|
print(f"[Voz] Anuncio en {c.cast_info.friendly_name} "
|
||||||
|
f"(vol {config.GOOGLE_TV_VOLUMEN})")
|
||||||
|
if _esperar_fin(c):
|
||||||
|
print(f"[Voz] Anuncio terminado en {c.cast_info.friendly_name}")
|
||||||
|
if config.GOOGLE_TV_APAGAR:
|
||||||
|
c.quit_app()
|
||||||
|
print(f"[Voz] {c.cast_info.friendly_name} apagada (standby)")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Voz] Error en {c.cast_info.friendly_name}: {e}")
|
print(f"[Voz] Error en {c.cast_info.friendly_name}: {e}")
|
||||||
finally:
|
finally:
|
||||||
pychromecast.discovery.stop_discovery(browser)
|
pychromecast.discovery.stop_discovery(browser)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Voz] Error de casting: {e}")
|
print(f"[Voz] Error de casting: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def reproducir_en_teles(texto):
|
||||||
|
anuncio_en_teles(texto)
|
||||||
|
|||||||
Reference in New Issue
Block a user