02-ago: [proyecto] vigilancia - secuencia TV: encender silencioso, espera 15s, sirena emergencia (WAV sintetizada), mensaje, apagado ADB

This commit is contained in:
minguezsanzjuanjose
2026-08-02 14:37:55 +02:00
parent f6745a7db1
commit 3723f39371
3 changed files with 110 additions and 9 deletions

View File

@@ -48,10 +48,15 @@ GOOGLE_TV_ACTIVO=1
GOOGLE_TV_NOMBRES= GOOGLE_TV_NOMBRES=
GOOGLE_TV_VOLUMEN=30 GOOGLE_TV_VOLUMEN=30
GOOGLE_TV_APAGAR=1 GOOGLE_TV_APAGAR=1
GOOGLE_TV_PITIDO_ACTIVO=1
GOOGLE_TV_ESPERA_SG=15
GOOGLE_TV_PITIDO_SG=6
VOZ_EDGE=es-ES-AlvaroNeural VOZ_EDGE=es-ES-AlvaroNeural
VOZ_PITCH=-15Hz VOZ_PITCH=-15Hz
VOZ_RATE=-10% VOZ_RATE=-10%
GOOGLE_TV_PUERTO=8500 GOOGLE_TV_PUERTO=8500
ADB_TV_IP=192.168.50.16:5555
GOOGLE_TV_APAGAR_VIA_ADB=1
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_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í. VOZ_MENSAJE_CONOCIDO={nombres}, ¿qué estás haciendo? No entres ahí.

View File

@@ -69,3 +69,6 @@ ADB_PATH = os.getenv(
) )
ADB_TV_IP = os.getenv("ADB_TV_IP", "192.168.50.16:5555") ADB_TV_IP = os.getenv("ADB_TV_IP", "192.168.50.16:5555")
GOOGLE_TV_APAGAR_VIA_ADB = os.getenv("GOOGLE_TV_APAGAR_VIA_ADB", "1") == "1" GOOGLE_TV_APAGAR_VIA_ADB = os.getenv("GOOGLE_TV_APAGAR_VIA_ADB", "1") == "1"
GOOGLE_TV_PITIDO_ACTIVO = os.getenv("GOOGLE_TV_PITIDO_ACTIVO", "1") == "1"
GOOGLE_TV_ESPERA_SG = int(os.getenv("GOOGLE_TV_ESPERA_SG", "15"))
GOOGLE_TV_PITIDO_SG = float(os.getenv("GOOGLE_TV_PITIDO_SG", "6"))

View File

@@ -106,13 +106,93 @@ def _esperar_fin(cast, timeout=90):
return False return False
def _generar_pitido():
"""Genera una sirena de emergencia (WAV) si no existe."""
ruta = os.path.join(MEDIA_DIR, "pitido_emergencia.wav")
if os.path.exists(ruta):
return ruta
try:
import numpy as np
import wave as _wave
os.makedirs(MEDIA_DIR, exist_ok=True)
sr = 44100
dur = config.GOOGLE_TV_PITIDO_SG
t = np.linspace(0, dur, int(sr * dur), endpoint=False)
freq = 500 + 400 * np.abs(np.sin(2 * np.pi * 0.5 * t))
fase = 2 * np.pi * np.cumsum(freq) / sr
y = 0.7 * np.sin(fase)
fade = min(0.2, dur / 4)
n_fade = int(fade * sr)
y[:n_fade] *= np.linspace(0, 1, n_fade)
y[-n_fade:] *= np.linspace(1, 0, n_fade)
data = (y * 32767).astype(np.int16)
with _wave.open(ruta, "w") as w:
w.setnchannels(1)
w.setsampwidth(2)
w.setframerate(sr)
w.writeframes(data.tobytes())
except Exception as e:
print(f"[Voz] No se pudo generar el pitido: {e}")
return ruta
def _generar_silencio():
"""Genera un WAV silencioso corto para encender la tele sin sonido."""
ruta = os.path.join(MEDIA_DIR, "silencio.wav")
if os.path.exists(ruta):
return ruta
try:
import wave as _wave
os.makedirs(MEDIA_DIR, exist_ok=True)
with _wave.open(ruta, "w") as w:
w.setnchannels(1)
w.setsampwidth(2)
w.setframerate(8000)
w.writeframes(b"\x00\x00" * (8000 * 2))
except Exception as e:
print(f"[Voz] No se pudo generar el silencio: {e}")
return ruta
def _url_pitido():
ruta = _generar_pitido()
if not os.path.exists(ruta):
return None
_arrancar_servidor()
if _server is None:
return None
return f"http://{_ip_lan()}:{config.GOOGLE_TV_PUERTO}/{os.path.basename(ruta)}"
def _url_silencio():
ruta = _generar_silencio()
if not os.path.exists(ruta):
return None
_arrancar_servidor()
if _server is None:
return None
return f"http://{_ip_lan()}:{config.GOOGLE_TV_PUERTO}/{os.path.basename(ruta)}"
def _reproducir_y_esperar(c, url, content_type, titulo, desc):
c.media_controller.play_media(url, content_type, title=titulo)
c.media_controller.block_until_active(timeout=10)
print(f"[Voz] {desc}")
return _esperar_fin(c)
def anuncio_en_teles(texto): def anuncio_en_teles(texto):
"""Enciende la tele con el volumen configurado, reproduce el mensaje y la apaga.""" """Enciende la tele (vol configurado), espera, sirena de emergencia,
mensaje y apaga por ADB."""
if not config.GOOGLE_TV_ACTIVO: if not config.GOOGLE_TV_ACTIVO:
return return
url = _url_audio(texto) url_mensaje = _url_audio(texto)
if url is None: if url_mensaje is None:
return return
url_pitido = _url_pitido() if config.GOOGLE_TV_PITIDO_ACTIVO else None
url_silencio = _url_silencio() if url_pitido else None
try: try:
import pychromecast import pychromecast
@@ -133,12 +213,25 @@ def anuncio_en_teles(texto):
try: try:
c.wait(timeout=10) c.wait(timeout=10)
c.set_volume(config.GOOGLE_TV_VOLUMEN / 100.0) c.set_volume(config.GOOGLE_TV_VOLUMEN / 100.0)
c.media_controller.play_media(url, "audio/mpeg", title=texto[:80])
c.media_controller.block_until_active(timeout=10) if url_silencio:
print(f"[Voz] Anuncio en {c.cast_info.friendly_name} " _reproducir_y_esperar(
c, url_silencio, "audio/wav", "inicio",
f"Tele encendida ({c.cast_info.friendly_name})")
if config.GOOGLE_TV_ESPERA_SG > 0:
print(f"[Voz] Esperando {config.GOOGLE_TV_ESPERA_SG}s...")
time.sleep(config.GOOGLE_TV_ESPERA_SG)
if url_pitido:
_reproducir_y_esperar(
c, url_pitido, "audio/wav", "pitido",
f"Pitido de emergencia en {c.cast_info.friendly_name}")
_reproducir_y_esperar(
c, url_mensaje, "audio/mpeg", texto[:80],
f"Anuncio en {c.cast_info.friendly_name} "
f"(vol {config.GOOGLE_TV_VOLUMEN})") 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_VIA_ADB: if config.GOOGLE_TV_APAGAR_VIA_ADB:
from tv_adb import apagar_tv from tv_adb import apagar_tv