02-ago: [proyecto] vigilancia - comando tv_mensaje.py (mensaje parametrizado) + API remota tv_api.py (puerto 8600, via Tailscale) + fix espera FINISHED + AGENTS.md comando tv y Regla 23

This commit is contained in:
minguezsanzjuanjose
2026-08-02 14:45:54 +02:00
parent e31478efc6
commit c15800fb10
5 changed files with 96 additions and 8 deletions

View File

@@ -57,6 +57,9 @@ VOZ_RATE=-10%
GOOGLE_TV_PUERTO=8500
ADB_TV_IP=192.168.50.16:5555
GOOGLE_TV_APAGAR_VIA_ADB=1
# API remota para enviar mensajes a la tele (tv_api.py). Acceso desde fuera via Tailscale.
TV_API_PUERTO=8600
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
vigilancia/tv_api.py Normal file
View File

@@ -0,0 +1,46 @@
import json
import os
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs, urlparse
sys_dir = os.path.dirname(os.path.abspath(__file__))
import sys
sys.path.insert(0, sys_dir)
import config
from voz import anuncio_en_teles
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
q = parse_qs(urlparse(self.path).query)
texto = (q.get("texto") or q.get("msg") or [""])[0]
if not texto:
self._resp(400, {"error": "falta parametro texto"})
return
threading.Thread(target=anuncio_en_teles, args=(texto,), daemon=True).start()
print(f"[TV-API] Mensaje lanzado: {texto[:80]}")
self._resp(200, {"ok": True, "mensaje": texto})
def _resp(self, code, obj):
body = json.dumps(obj).encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *a):
pass
def main():
puerto = int(os.getenv("TV_API_PUERTO", "8600"))
print(f"TV API escuchando en :{puerto}")
print(f"Uso: http://<ip>:{puerto}/tv/mensaje?texto=Mensaje")
HTTPServer(("0.0.0.0", puerto), Handler).serve_forever()
if __name__ == "__main__":
main()

21
vigilancia/tv_mensaje.py Normal file
View File

@@ -0,0 +1,21 @@
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import config
from voz import anuncio_en_teles
def main():
texto = " ".join(sys.argv[1:])
if not texto:
print("Uso: python tv_mensaje.py \"mensaje a la tele\"")
print("Ejemplo: python tv_mensaje.py \"A cenar, la comida está lista\"")
return
print(f"Enviando a la tele: {texto}")
anuncio_en_teles(texto)
if __name__ == "__main__":
main()

View File

@@ -109,8 +109,8 @@ def _esperar_reproduccion(cast, timeout=15):
return False
def _esperar_fin(cast, timeout=90):
"""Espera a que termine (IDLE) despues de haber empezado a reproducir."""
def _esperar_fin(cast, timeout=120):
"""Espera a que el audio termine de verdad (IDLE + idle_reason FINISHED)."""
mc = cast.media_controller
t0 = time.time()
visto_playing = False
@@ -119,15 +119,12 @@ def _esperar_fin(cast, timeout=90):
st = mc.status
if st.player_state == "PLAYING":
visto_playing = True
if st.player_state == "IDLE":
if st.idle_reason == "FINISHED":
return True
if visto_playing:
return True
if st.player_state == "IDLE" and st.idle_reason == "FINISHED":
return True
except Exception:
pass
time.sleep(1)
return False
return visto_playing
def _generar_pitido():