From 75de5c613ff673fd3f405c1810e7125eff703d76 Mon Sep 17 00:00:00 2001 From: minguezsanzjuanjose Date: Sun, 2 Aug 2026 13:17:45 +0200 Subject: [PATCH] 02-ago: [proyecto] vigilancia - integra middleware Alexa (alexa_notify.js/VoiceMonkey) + .env con credenciales de Telegram y VoiceMonkey + tests 18/18 --- vigilancia/.env.example | 6 ++++++ vigilancia/config.py | 4 ++++ vigilancia/notificador.py | 20 +++++++++++++++++++ vigilancia/test_prueba.py | 41 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/vigilancia/.env.example b/vigilancia/.env.example index 61460ec..ed0710c 100644 --- a/vigilancia/.env.example +++ b/vigilancia/.env.example @@ -18,5 +18,11 @@ COOLDOWN=45 # Webhook Alexa/Node-RED (opcional, dejar vacío si no se usa) WEBHOOK_ALEXA= +# Middleware VoiceMonkey (reutiliza scripts/alexa_notify.js de biblioteca_negocio_prolongo) +VOICEMONKEY_TOKEN= +VOICEMONKEY_DEVICE= +ALEXA_NODE=node +ALEXA_NOTIFY_SCRIPT=C:\Users\juanm\Documents\GitHub\biblioteca_negocio_prolongo\scripts\alexa_notify.js + # Directorio de snapshots SNAPSHOT_DIR=snapshots diff --git a/vigilancia/config.py b/vigilancia/config.py index d629eb6..3b04061 100644 --- a/vigilancia/config.py +++ b/vigilancia/config.py @@ -27,4 +27,8 @@ HORA_INICIO = int(os.getenv("HORA_INICIO", 0)) HORA_FIN = int(os.getenv("HORA_FIN", 23)) COOLDOWN = int(os.getenv("COOLDOWN", 30)) WEBHOOK_ALEXA = os.getenv("WEBHOOK_ALEXA", "").strip() +VOICEMONKEY_TOKEN = os.getenv("VOICEMONKEY_TOKEN", "").strip() +VOICEMONKEY_DEVICE = os.getenv("VOICEMONKEY_DEVICE", "").strip() +ALEXA_NODE = os.getenv("ALEXA_NODE", "node") +ALEXA_NOTIFY_SCRIPT = os.getenv("ALEXA_NOTIFY_SCRIPT", "").strip() SNAPSHOT_DIR = os.getenv("SNAPSHOT_DIR", "snapshots") diff --git a/vigilancia/notificador.py b/vigilancia/notificador.py index 8de5d04..3d87d1d 100644 --- a/vigilancia/notificador.py +++ b/vigilancia/notificador.py @@ -1,3 +1,6 @@ +import os +import subprocess + import requests import config @@ -24,6 +27,23 @@ def enviar_telegram(ruta_imagen: str, texto: str): def disparar_webhook_alexa(texto: str): + if config.ALEXA_NOTIFY_SCRIPT: + env = os.environ.copy() + if config.VOICEMONKEY_TOKEN: + env["VOICEMONKEY_TOKEN"] = config.VOICEMONKEY_TOKEN + if config.VOICEMONKEY_DEVICE: + env["VOICEMONKEY_DEVICE"] = config.VOICEMONKEY_DEVICE + try: + r = subprocess.run( + [config.ALEXA_NODE, config.ALEXA_NOTIFY_SCRIPT, texto], + env=env, timeout=15, capture_output=True, text=True, + ) + if r.returncode != 0: + print(f"[Alexa] Error middleware: {r.stderr[:200]}") + except Exception as e: + print(f"[Alexa] Excepción lanzando middleware: {e}") + return + if not config.WEBHOOK_ALEXA: return diff --git a/vigilancia/test_prueba.py b/vigilancia/test_prueba.py index 21e54e5..c559166 100644 --- a/vigilancia/test_prueba.py +++ b/vigilancia/test_prueba.py @@ -142,12 +142,15 @@ def test_webhook_mock(): real = notificador.requests notificador.requests = FakeRequests() orig = config.WEBHOOK_ALEXA + orig_script = config.ALEXA_NOTIFY_SCRIPT config.WEBHOOK_ALEXA = "http://localhost:1880/alexa" + config.ALEXA_NOTIFY_SCRIPT = "" try: disparar_webhook_alexa("PRUEBA") check("Webhook disparado cuando configurado", len(llamadas) == 1) finally: config.WEBHOOK_ALEXA = orig + config.ALEXA_NOTIFY_SCRIPT = orig_script notificador.requests = real llamadas.clear() @@ -172,6 +175,43 @@ def test_yolo(): res is False) +def test_alexa_middleware(): + print("\n--- RF-08: Middleware alexa_notify.js (mock subprocess) ---") + llamadas = {} + + def fake_run(cmd, **kwargs): + llamadas["cmd"] = cmd + llamadas["env_token"] = kwargs.get("env", {}).get("VOICEMONKEY_TOKEN", "") + class R: + returncode = 0 + stderr = "" + return R() + + import notificador + real_run = notificador.subprocess.run + notificador.subprocess.run = fake_run + orig_script = config.ALEXA_NOTIFY_SCRIPT + orig_token = config.VOICEMONKEY_TOKEN + orig_dev = config.VOICEMONKEY_DEVICE + orig_node = config.ALEXA_NODE + config.ALEXA_NOTIFY_SCRIPT = "alexa_notify.js" + config.VOICEMONKEY_TOKEN = "VM_TOKEN_FAKE" + config.VOICEMONKEY_DEVICE = "echo-salon" + config.ALEXA_NODE = "node" + try: + disparar_webhook_alexa("PRUEBA movimiento") + check("Ejecuta node + script + texto", + llamadas.get("cmd") == ["node", "alexa_notify.js", "PRUEBA movimiento"]) + check("Inyecta VOICEMONKEY_TOKEN al proceso", + llamadas.get("env_token") == "VM_TOKEN_FAKE") + finally: + notificador.subprocess.run = real_run + config.ALEXA_NOTIFY_SCRIPT = orig_script + config.VOICEMONKEY_TOKEN = orig_token + config.VOICEMONKEY_DEVICE = orig_dev + config.ALEXA_NODE = orig_node + + def test_flujo_completo(): print("\n--- RF-03/RF-04/RF-05: Flujo completo snapshot+cooldown (mock) ---") os.makedirs(config.SNAPSHOT_DIR, exist_ok=True) @@ -202,6 +242,7 @@ if __name__ == "__main__": test_franja_horaria() test_telegram_mock() test_webhook_mock() + test_alexa_middleware() test_yolo() test_flujo_completo() print(f"\n=== RESULTADO: {APROBADOS} OK, {FALLIDOS} FALLO ===")