From dff7262ebd65250532f4035d608c72ad1afe497e Mon Sep 17 00:00:00 2001 From: juanminsanz Date: Sun, 20 Sep 2026 21:12:35 +0200 Subject: [PATCH] 20-sep: Home Assistant en VPS para control luces Alexa (gratis) --- docs/configuracion-voicemonkey-luces.md | 62 ++++++ docs/home-assistant-setup-alexa.md | 217 +++++++++++++++++++++ scripts/configurar_voicemonkey.py | 85 ++++++++ scripts/monitor_homeassistant.py | 98 ++++++++++ scripts/protocolo_apagado.py | 75 +++++++ scripts/protocolo_apagado_homeassistant.py | 126 ++++++++++++ scripts/protocolo_apagado_test.py | 126 ++++++++++++ scripts/test_homeassistant.py | 75 +++++++ 8 files changed, 864 insertions(+) create mode 100644 docs/configuracion-voicemonkey-luces.md create mode 100644 docs/home-assistant-setup-alexa.md create mode 100644 scripts/configurar_voicemonkey.py create mode 100644 scripts/monitor_homeassistant.py create mode 100644 scripts/protocolo_apagado.py create mode 100644 scripts/protocolo_apagado_homeassistant.py create mode 100644 scripts/protocolo_apagado_test.py create mode 100644 scripts/test_homeassistant.py diff --git a/docs/configuracion-voicemonkey-luces.md b/docs/configuracion-voicemonkey-luces.md new file mode 100644 index 0000000..845ed18 --- /dev/null +++ b/docs/configuracion-voicemonkey-luces.md @@ -0,0 +1,62 @@ +# Configuración VoiceMonkey para luces Alexa + +## Resumen +VoiceMonkey permite disparar rutinas de Alexa desde scripts Python mediante HTTP triggers. + +## Configuración necesaria en VoiceMonkey + +### 1. Crear "Monkeys" (triggers virtuales) +Ir a https://voicemonkey.io/ → Dashboard → Add New Monkey + +#### Monkey 1: `apagar-barras` +- **Name**: apagar-barras +- **Routine**: "Alexa, apaga barras" +- **Device**: (el Echo que ejecutará el comando, ej: echo-dot-de-nahid-ody8g) + +#### Monkey 2: `apagar-cuartillo` +- **Name**: apagar-cuartillo +- **Routine**: "Alexa, apaga cuartillo" +- **Device**: (el mismo Echo) + +### 2. Variables de entorno (.env) +``` +VOICEMONKEY_TOKEN= +VOICEMONKEY_DEVICE= +``` + +El token se obtiene en VoiceMonkey Dashboard → Settings → API Token. + +## Uso desde Python + +```python +import urllib.request +import os + +token = os.getenv("VOICEMONKEY_TOKEN") +url = f"https://api-v2.voicemonkey.io/trigger?token={token}&device=apagar-barras" + +req = urllib.request.Request(url) +with urllib.request.urlopen(req, timeout=10) as response: + if response.status == 200: + print("Luz apagada") +``` + +## Scripts que usan VoiceMonkey + +- `INFRAESTRUCTURA/scripts/protocolo_apagado.py` — apaga barras + cuartillo al apagar el PC +- `INFRAESTRUCTURA/scripts/protocolo_apagado_test.py` — versión de prueba sin shutdown + +## Notas + +- Las luces "barras" y "cuartillo" deben estar configuradas en la app Alexa como dispositivos smart home +- VoiceMonkey actúa como puente: recibe HTTP request → dispara rutina de Alexa → Alexa ejecuta comando de voz +- Si las luces no se apagan, verificar: + 1. Que los monkeys existen en VoiceMonkey + 2. Que las rutinas de Alexa están activas + 3. Que el token es correcto + 4. Que el Echo está online + +## Referencias + +- VoiceMonkey: https://voicemonkey.io/ +- API docs: https://voicemonkey.io/docs diff --git a/docs/home-assistant-setup-alexa.md b/docs/home-assistant-setup-alexa.md new file mode 100644 index 0000000..fe0388b --- /dev/null +++ b/docs/home-assistant-setup-alexa.md @@ -0,0 +1,217 @@ +# Home Assistant + Alexa: Guía completa de configuración + +## Resumen + +Home Assistant instalado en el VPS Contabo para controlar luces de Alexa ("barras" y "cuartillo") de forma gratuita e ilimitada, reemplazando VoiceMonkey. + +--- + +## 1. Instalación (YA HECHA) + +Home Assistant corre en el VPS en modo `network_mode: host`: + +```yaml +# /opt/homeassistant/docker-compose.yml +services: + homeassistant: + container_name: homeassistant + image: homeassistant/home-assistant:2025.1 + restart: unless-stopped + network_mode: host + environment: + - TZ=Europe/Madrid + volumes: + - ./config:/config +``` + +**URL de acceso:** http://185.187.169.109:8123 + +--- + +## 2. Configuración inicial (WIZARD) + +Al acceder por primera vez a http://185.187.169.109:8123: + +1. **Crear cuenta admin:** + - Nombre: `admin` (o el que quieras) + - Usuario: `admin` + - Contraseña: (guardarla en Engram) + +2. **Configuración básica:** + - Nombre de la casa: `Casa Juanjo` (o el que quieras) + - Ubicación: Madrid, España + - Zona horaria: Europe/Madrid (ya configurada) + - Unidades: Métricas + +3. **Saltar la detección automática de dispositivos** (lo haremos manualmente) + +--- + +## 3. Instalar HACS (Home Assistant Community Store) + +HACS es necesario para instalar Alexa Media Player. + +### Método 1: Script automático (RECOMENDADO) + +```bash +# Desde el VPS +docker exec -it homeassistant bash +wget -O - https://get.hacs.xyz | bash - +exit +docker restart homeassistant +``` + +### Método 2: Manual + +1. Acceder a http://185.187.169.109:8123 +2. Settings → Devices & Services → Add Integration +3. Buscar "HACS" +4. Seguir el wizard (pide vincular con GitHub) + +**Reiniciar Home Assistant después de instalar HACS.** + +--- + +## 4. Instalar Alexa Media Player + +1. **Abrir HACS:** + - Settings → Devices & Services → HACS + +2. **Buscar "Alexa Media Player":** + - HACS → Integrations → Explore & Download Repositories + - Buscar: `Alexa Media Player` + - Download + +3. **Reiniciar Home Assistant:** + - Settings → System → Restart + +4. **Añadir la integración:** + - Settings → Devices & Services → Add Integration + - Buscar: `Alexa Media Player` + - Introducir credenciales de Amazon (usuario/contraseña) + - **Si pide 2FA:** introducir el código que llegue al móvil/email + +5. **Verificar dispositivos detectados:** + - Settings → Devices & Services → Alexa Media Player + - Debería aparecer el Echo Dot y las luces "barras" y "cuartillo" + +--- + +## 5. Generar API Token (Long-Lived Access Token) + +1. **Ir al perfil de usuario:** + - Click en tu nombre (esquina inferior izquierda) + - Scroll hasta "Long-Lived Access Tokens" + +2. **Crear token:** + - Click en "Create Token" + - Nombre: `Protocolo Apagado PC` + - Copiar el token (aparece una sola vez) + +3. **Guardar el token en .env:** + ``` + HOMEASSISTANT_URL=http://185.187.169.109:8123 + HOMEASSISTANT_TOKEN= + ``` + +--- + +## 6. Probar el control de luces (desde el PC) + +### Script de prueba Python: + +```python +import requests +import os +from dotenv import load_dotenv + +load_dotenv() + +url = os.getenv("HOMEASSISTANT_URL") +token = os.getenv("HOMEASSISTANT_TOKEN") + +headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" +} + +# Apagar "barras" +response = requests.post( + f"{url}/api/services/light/turn_off", + headers=headers, + json={"entity_id": "light.barras"} +) +print(f"Barras: {response.status_code}") + +# Apagar "cuartillo" +response = requests.post( + f"{url}/api/services/light/turn_off", + headers=headers, + json={"entity_id": "light.cuartillo"} +) +print(f"Cuartillo: {response.status_code}") +``` + +**Ejecutar:** +```powershell +python C:\Users\juanm\Documents\Github\INFRAESTRUCTURA\scripts\test_homeassistant.py +``` + +**Resultado esperado:** +``` +Barras: 200 +Cuartillo: 200 +``` + +--- + +## 7. Actualizar el protocolo de apagado + +Una vez verificado que funciona, actualizar `protocolo_apagado.py` y `protocolo_apagado_test.py` para usar Home Assistant en lugar de VoiceMonkey. + +El script final está en `INFRAESTRUCTURA/scripts/protocolo_apagado_homeassistant.py`. + +--- + +## 8. Troubleshooting + +### Problema: Alexa Media Player no detecta las luces + +**Solución:** +1. Verificar que las luces estén configuradas en la app Alexa +2. Ir a Settings → Devices & Services → Alexa Media Player → Options +3. Marcar "Include devices" (por defecto solo incluye Echos) +4. Reload integration + +### Problema: Entity IDs incorrectos + +Las luces pueden tener IDs diferentes. Para verificar: + +```python +import requests +url = "http://185.187.169.109:8123/api/states" +headers = {"Authorization": "Bearer "} +response = requests.get(url, headers=headers) +lights = [e for e in response.json() if e["entity_id"].startswith("light.")] +for light in lights: + print(light["entity_id"], light["attributes"].get("friendly_name")) +``` + +### Problema: 401 Unauthorized + +- Token incorrecto o expirado +- Regenerar el token en Home Assistant + +### Problema: Alexa pide re-autenticación + +- Login caducado (cada ~2 semanas) +- Re-login en Settings → Devices & Services → Alexa Media Player → Configure + +--- + +## Referencias + +- Home Assistant: https://www.home-assistant.io/ +- Alexa Media Player: https://github.com/custom-components/alexa_media_player +- HACS: https://hacs.xyz/ +- API REST: https://developers.home-assistant.io/docs/api/rest/ diff --git a/scripts/configurar_voicemonkey.py b/scripts/configurar_voicemonkey.py new file mode 100644 index 0000000..1e79990 --- /dev/null +++ b/scripts/configurar_voicemonkey.py @@ -0,0 +1,85 @@ +""" +Script auxiliar para configurar VoiceMonkey para el protocolo de apagado. + +Este script NO puede crear automáticamente los monkeys (VoiceMonkey no tiene API pública para eso), +pero te guía paso a paso para crearlos manualmente en la web. +""" +import os +import sys +from dotenv import load_dotenv + +# Fix encoding para Windows +if sys.platform == 'win32': + import codecs + sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') + sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') + +# Cargar .env +dotenv_path = r"C:\Users\juanm\Documents\GitHub\biblioteca_negocio_prolongo\.env" +load_dotenv(dotenv_path) + +def main(): + print("=" * 70) + print(" CONFIGURACIÓN DE VOICEMONKEY PARA PROTOCOLO DE APAGADO") + print("=" * 70) + print() + + token = os.getenv("VOICEMONKEY_TOKEN") + device = os.getenv("VOICEMONKEY_DEVICE") + + if not token: + print("[X] ERROR: Falta VOICEMONKEY_TOKEN en .env") + print() + print("1. Ve a https://voicemonkey.io/") + print("2. Inicia sesion con tu cuenta de Amazon") + print("3. Ve a Settings -> API Token") + print("4. Copia el token y anadelo al .env:") + print(" VOICEMONKEY_TOKEN=") + return + + print(f"[OK] Token encontrado: {token[:20]}...") + print(f"[OK] Device: {device}") + print() + print("=" * 70) + print(" INSTRUCCIONES PARA CREAR LOS MONKEYS") + print("=" * 70) + print() + print("1. Abre https://voicemonkey.io/monkeys en tu navegador") + print() + print("2. Haz clic en 'Add New Monkey'") + print() + print("3. MONKEY 1: apagar-barras") + print(" - Monkey ID: apagar-barras") + print(" - Device: " + (device or "")) + print(" - Action: Announcement") + print(" - Text: (vacio)") + print() + print("4. Guarda y crea una RUTINA en la app Alexa:") + print(" - Nombre: Apagar barras") + print(" - Cuando: Voz (di 'Alexa, apagar barras')") + print(" - Accion: Smart Home -> Apagar 'barras'") + print() + print("5. MONKEY 2: apagar-cuartillo") + print(" - Monkey ID: apagar-cuartillo") + print(" - Device: " + (device or "")) + print(" - Action: Announcement") + print(" - Text: (vacio)") + print() + print("6. Guarda y crea otra RUTINA en la app Alexa:") + print(" - Nombre: Apagar cuartillo") + print(" - Cuando: Voz (di 'Alexa, apagar cuartillo')") + print(" - Accion: Smart Home -> Apagar 'cuartillo'") + print() + print("=" * 70) + print(" VERIFICACIÓN") + print("=" * 70) + print() + print("Una vez creados los monkeys y las rutinas, prueba:") + print() + print(" python protocolo_apagado_test.py") + print() + print("Las luces 'barras' y 'cuartillo' deberían apagarse.") + print() + +if __name__ == "__main__": + main() diff --git a/scripts/monitor_homeassistant.py b/scripts/monitor_homeassistant.py new file mode 100644 index 0000000..8626b7c --- /dev/null +++ b/scripts/monitor_homeassistant.py @@ -0,0 +1,98 @@ +""" +Monitor que verifica cada 30 segundos si Home Assistant esta listo. +Cuando este accesible, muestra un mensaje box y emite aviso sonoro. + +Uso: + python monitor_homeassistant.py +""" +import time +import subprocess +import sys +import os + +# Fix encoding para Windows +if sys.platform == 'win32': + import codecs + sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') + sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') + +def check_homeassistant(): + """Verifica si Home Assistant esta corriendo en el VPS""" + try: + result = subprocess.run( + ["ssh", "-i", r"C:\Users\juanm\Documents\GitHub\contabo", + "root@185.187.169.109", + "docker ps | grep homeassistant | grep -q Up && echo OK || echo NOK"], + capture_output=True, + timeout=10, + text=True + ) + return "OK" in result.stdout + except Exception: + return False + +def mostrar_aviso(): + """Muestra message box y emite aviso sonoro""" + # Message box + ps_messagebox = """ +Add-Type -AssemblyName System.Windows.Forms +[System.Windows.Forms.MessageBox]::Show( + "Home Assistant esta listo en http://185.187.169.109:8123`n`nPasos siguientes:`n1. Crear cuenta admin`n2. Instalar HACS`n3. Instalar Alexa Media Player`n4. Generar API Token`n`nVer: INFRAESTRUCTURA/docs/home-assistant-setup-alexa.md", + "Home Assistant Listo", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Information +) +""" + subprocess.run(["powershell", "-STA", "-Command", ps_messagebox]) + + # Aviso sonoro + try: + subprocess.run([ + "python", + r"C:\Users\juanm\Documents\GitHub\biblioteca_negocio_prolongo\scripts\aviso_sonoro.py", + "ok" + ]) + except Exception: + pass + +def main(): + print("=" * 70) + print(" MONITOR HOME ASSISTANT - Verificando cada 30 segundos") + print("=" * 70) + print() + print("Esperando a que Home Assistant este listo...") + print("URL: http://185.187.169.109:8123") + print() + + intentos = 0 + max_intentos = 60 # 30 minutos max + + while intentos < max_intentos: + if check_homeassistant(): + print() + print("=" * 70) + print(" HOME ASSISTANT LISTO!") + print("=" * 70) + print() + print("Accede a: http://185.187.169.109:8123") + print("Documentacion: INFRAESTRUCTURA/docs/home-assistant-setup-alexa.md") + print() + mostrar_aviso() + return + + intentos += 1 + tiempo_restante = (max_intentos - intentos) * 30 // 60 + print(f"[{intentos}/{max_intentos}] Aun no esta listo... (max {tiempo_restante} min)") + time.sleep(30) + + print() + print("=" * 70) + print(" TIMEOUT - Home Assistant no arranque en 30 minutos") + print("=" * 70) + print() + print("Verificar manualmente:") + print(" ssh -i C:\\Users\\juanm\\Documents\\GitHub\\contabo root@185.187.169.109") + print(" cd /opt/homeassistant && docker compose logs -f") + +if __name__ == "__main__": + main() diff --git a/scripts/protocolo_apagado.py b/scripts/protocolo_apagado.py new file mode 100644 index 0000000..9c16dd8 --- /dev/null +++ b/scripts/protocolo_apagado.py @@ -0,0 +1,75 @@ +import os +import sys +import time +import subprocess +import urllib.request +from dotenv import load_dotenv + +# Cargar variables de entorno locales (.env de biblioteca_negocio_prolongo) +dotenv_path = r"C:\Users\juanm\Documents\GitHub\biblioteca_negocio_prolongo\.env" +load_dotenv(dotenv_path) + +def cerrar_ventanas(): + print("1. Cerrando ventanas abiertas (excepto terminal de OpenCode)...") + ps_command = """ + $procesos = Get-Process | Where-Object { $_.MainWindowHandle -ne 0 -and $_.ProcessName -notmatch 'OpenCode|WindowsTerminal|cmd|powershell|Code|conhost' } + foreach ($p in $procesos) { + try { + $p.CloseMainWindow() | Out-Null + Start-Sleep -Milliseconds 200 + if (-not $p.HasExited) { $p.Kill() } + } catch {} + } + """ + subprocess.run(["powershell", "-Command", ps_command], capture_output=True) + print(" -> Ventanas cerradas.") + +def apagar_luces_alexa(): + """Apaga las luces 'barras' y 'cuartillo' via VoiceMonkey""" + print("2. Apagando luces de Alexa (barras + cuartillo)...") + token = os.getenv("VOICEMONKEY_TOKEN") + if not token: + print(" -> [ERROR] Falta VOICEMONKEY_TOKEN en .env") + return + + # VoiceMonkey: crear dos "monkeys" (triggers virtuales) llamados 'apagar-barras' y 'apagar-cuartillo' + # que en su rutina de Alexa ejecutan: "Alexa, apaga barras" y "Alexa, apaga cuartillo" + + luces = [ + ("barras", "apagar-barras"), + ("cuartillo", "apagar-cuartillo") + ] + + for nombre, monkey in luces: + url = f"https://api-v2.voicemonkey.io/trigger?token={token}&device={monkey}" + try: + req = urllib.request.Request(url) + with urllib.request.urlopen(req, timeout=10) as response: + if response.status == 200: + print(f" -> Señal enviada para apagar '{nombre}'") + else: + print(f" -> Error HTTP {response.status} al apagar '{nombre}'") + except Exception as e: + print(f" -> Error al apagar '{nombre}': {e}") + +def apagar_pantalla_tapo(): + print("3. Apagando pantalla (Tapo) via túnel RPi...") + # Ejecutamos el apagado del enchufe enviando el comando a través del VPS a la RPi + cmd = '''ssh -i C:\\Users\\juanm\\Documents\\GitHub\\contabo root@185.187.169.109 "ssh -i /root/.ssh/pi_deploy -p 2222 -o StrictHostKeyChecking=no pi@127.0.0.1 'python3 -c \\\"from PyP100 import PyP100; p100 = PyP100.P100(\\'192.168.50.224\\', \\'hermes211@gmail.com\\', \\'Nelo@36666\\'); p100.handshake(); p100.login(); p100.turnOff()\\\"'"''' + try: + subprocess.run(cmd, shell=True, capture_output=True, timeout=15) + print(" -> Pantalla apagada.") + except Exception as e: + print(f" -> Error al apagar pantalla: {e}") + +def shutdown_pc(): + print("4. Apagando el PC en 10 segundos...") + os.system("shutdown /s /t 10") + +if __name__ == "__main__": + print("=== INICIANDO PROTOCOLO DE APAGADO ===") + cerrar_ventanas() + apagar_luces_alexa() + apagar_pantalla_tapo() + shutdown_pc() + print("=== PROTOCOLO COMPLETADO ===") diff --git a/scripts/protocolo_apagado_homeassistant.py b/scripts/protocolo_apagado_homeassistant.py new file mode 100644 index 0000000..446a9dd --- /dev/null +++ b/scripts/protocolo_apagado_homeassistant.py @@ -0,0 +1,126 @@ +""" +Protocolo de apagado completo del PC usando HOME ASSISTANT (version PRODUCCION). + +Pasos: +1. Cerrar ventanas abiertas +2. Apagar luces Alexa via Home Assistant: "barras" y "cuartillo" +3. Apagar pantalla (Tapo via tunel RPi) +4. Shutdown del PC en 10 segundos + +Uso: + python protocolo_apagado_homeassistant.py +""" +import os +import sys +import subprocess +import requests +from dotenv import load_dotenv + +# Fix encoding para Windows +if sys.platform == 'win32': + import codecs + sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') + sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') + +# Cargar variables de entorno locales (.env de biblioteca_negocio_prolongo) +dotenv_path = r"C:\Users\juanm\Documents\GitHub\biblioteca_negocio_prolongo\.env" +load_dotenv(dotenv_path) + +def cerrar_ventanas(): + """Cierra todas las ventanas excepto terminal/OpenCode""" + print("1. Cerrando ventanas abiertas (excepto terminal de OpenCode)...") + ps_command = """ + $procesos = Get-Process | Where-Object { $_.MainWindowHandle -ne 0 -and $_.ProcessName -notmatch 'OpenCode|WindowsTerminal|cmd|powershell|Code|conhost' } + foreach ($p in $procesos) { + try { + $p.CloseMainWindow() | Out-Null + Start-Sleep -Milliseconds 200 + if (-not $p.HasExited) { $p.Kill() } + } catch {} + } + """ + subprocess.run(["powershell", "-Command", ps_command], capture_output=True) + print(" [OK] Ventanas cerradas.") + +def apagar_luces_homeassistant(): + """Apaga las luces 'barras' y 'cuartillo' via Home Assistant REST API""" + print("2. Apagando luces de Alexa via Home Assistant (barras + cuartillo)...") + + url = os.getenv("HOMEASSISTANT_URL") + token = os.getenv("HOMEASSISTANT_TOKEN") + + if not url or not token: + print(" [ERROR] Falta HOMEASSISTANT_URL o HOMEASSISTANT_TOKEN en .env") + return + + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + } + + luces = ["barras", "cuartillo"] + + for luz in luces: + entity_id = f"light.{luz}" + try: + response = requests.post( + f"{url}/api/services/light/turn_off", + headers=headers, + json={"entity_id": entity_id}, + timeout=10 + ) + + if response.status_code == 200: + print(f" [OK] Senial enviada para apagar '{luz}'") + else: + print(f" [ERROR] Error HTTP {response.status_code} al apagar '{luz}'") + except Exception as e: + print(f" [ERROR] Error al apagar '{luz}': {e}") + +def apagar_pantalla_tapo(): + """Apaga el enchufe Tapo de la pantalla via tunel RPi""" + print("3. Apagando pantalla (Tapo) via tunel RPi...") + + # Script Python que se ejecutara en la RPi + tapo_script = """from PyP100 import PyP100 +p100 = PyP100.P100('192.168.50.224', 'hermes211@gmail.com', 'Nelo@36666') +p100.handshake() +p100.login() +p100.turnOff()""" + + # Comando SSH en dos saltos: local -> VPS -> RPi + cmd = [ + "ssh", "-i", r"C:\Users\juanm\Documents\GitHub\contabo", + "root@185.187.169.109", + f"ssh -i /root/.ssh/pi_deploy -p 2222 -o StrictHostKeyChecking=no pi@127.0.0.1 'python3 -c \"{tapo_script}\"'" + ] + + try: + result = subprocess.run(cmd, capture_output=True, timeout=15, text=True) + if result.returncode == 0: + print(" [OK] Pantalla apagada.") + else: + print(f" [ERROR] Error al apagar pantalla (codigo {result.returncode})") + if result.stderr: + print(f" stderr: {result.stderr.strip()}") + except subprocess.TimeoutExpired: + print(" [ERROR] Timeout al apagar pantalla (>15s)") + except Exception as e: + print(f" [ERROR] Excepcion al apagar pantalla: {e}") + +def shutdown_pc(): + """Apaga el PC en 10 segundos""" + print("4. Apagando el PC en 10 segundos...") + os.system("shutdown /s /t 10") + +if __name__ == "__main__": + print("=" * 60) + print(" PROTOCOLO DE APAGADO (HOME ASSISTANT)") + print("=" * 60) + cerrar_ventanas() + apagar_luces_homeassistant() + apagar_pantalla_tapo() + shutdown_pc() + print("=" * 60) + print(" PROTOCOLO COMPLETADO") + print("=" * 60) diff --git a/scripts/protocolo_apagado_test.py b/scripts/protocolo_apagado_test.py new file mode 100644 index 0000000..a594fdd --- /dev/null +++ b/scripts/protocolo_apagado_test.py @@ -0,0 +1,126 @@ +""" +Protocolo de apagado completo del PC (VERSION DE PRUEBA - SIN SHUTDOWN) + +Pasos: +1. Cerrar ventanas abiertas +2. Apagar luces Alexa: "barras" y "cuartillo" (via VoiceMonkey) +3. Apagar pantalla (Tapo via tunel RPi) +4. [COMENTADO] Shutdown del PC + +Uso: + python protocolo_apagado_test.py +""" +import os +import sys +import time +import subprocess +import urllib.request +from dotenv import load_dotenv + +# Fix encoding para Windows +if sys.platform == 'win32': + import codecs + sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') + sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') + +# Cargar variables de entorno locales (.env de biblioteca_negocio_prolongo) +dotenv_path = r"C:\Users\juanm\Documents\GitHub\biblioteca_negocio_prolongo\.env" +load_dotenv(dotenv_path) + +def cerrar_ventanas(): + """Cierra todas las ventanas excepto terminal/OpenCode""" + print("1. Cerrando ventanas abiertas (excepto terminal de OpenCode)...") + ps_command = """ + $procesos = Get-Process | Where-Object { $_.MainWindowHandle -ne 0 -and $_.ProcessName -notmatch 'OpenCode|WindowsTerminal|cmd|powershell|Code|conhost' } + foreach ($p in $procesos) { + try { + $p.CloseMainWindow() | Out-Null + Start-Sleep -Milliseconds 200 + if (-not $p.HasExited) { $p.Kill() } + } catch {} + } + """ + subprocess.run(["powershell", "-Command", ps_command], capture_output=True) + print(" ✓ Ventanas cerradas.") + +def apagar_luces_alexa(): + """Apaga las luces 'barras' y 'cuartillo' via VoiceMonkey""" + print("2. Apagando luces de Alexa (barras + cuartillo)...") + token = os.getenv("VOICEMONKEY_TOKEN") + if not token: + print(" ✗ [ERROR] Falta VOICEMONKEY_TOKEN en .env") + return + + # VoiceMonkey: crear dos "monkeys" (triggers virtuales) llamados 'apagar-barras' y 'apagar-cuartillo' + # que en su rutina de Alexa ejecutan: "Alexa, apaga barras" y "Alexa, apaga cuartillo" + + luces = [ + ("barras", "apagar-barras"), + ("cuartillo", "apagar-cuartillo") + ] + + for nombre, monkey in luces: + url = f"https://api-v2.voicemonkey.io/trigger?token={token}&device={monkey}" + try: + req = urllib.request.Request(url) + with urllib.request.urlopen(req, timeout=10) as response: + if response.status == 200: + print(f" ✓ Señal enviada para apagar '{nombre}'") + else: + print(f" ✗ Error HTTP {response.status} al apagar '{nombre}'") + except Exception as e: + print(f" ✗ Error al apagar '{nombre}': {e}") + + print(" ℹ️ Si las luces no se apagaron, verifica que existan los monkeys:") + print(" - 'apagar-barras' → rutina Alexa: 'Alexa, apaga barras'") + print(" - 'apagar-cuartillo' → rutina Alexa: 'Alexa, apaga cuartillo'") + print(" en https://voicemonkey.io/") + +def apagar_pantalla_tapo(): + """Apaga el enchufe Tapo de la pantalla via tunel RPi""" + print("3. Apagando pantalla (Tapo) via túnel RPi...") + + # Script Python que se ejecutará en la RPi + tapo_script = """from PyP100 import PyP100 +p100 = PyP100.P100('192.168.50.224', 'hermes211@gmail.com', 'Nelo@36666') +p100.handshake() +p100.login() +p100.turnOff()""" + + # Comando SSH en dos saltos: local -> VPS -> RPi + cmd = [ + "ssh", "-i", r"C:\Users\juanm\Documents\GitHub\contabo", + "root@185.187.169.109", + f"ssh -i /root/.ssh/pi_deploy -p 2222 -o StrictHostKeyChecking=no pi@127.0.0.1 'python3 -c \"{tapo_script}\"'" + ] + + try: + result = subprocess.run(cmd, capture_output=True, timeout=15, text=True) + if result.returncode == 0: + print(" ✓ Pantalla apagada.") + else: + print(f" ✗ Error al apagar pantalla (código {result.returncode})") + if result.stderr: + print(f" stderr: {result.stderr.strip()}") + except subprocess.TimeoutExpired: + print(" ✗ Timeout al apagar pantalla (>15s)") + except Exception as e: + print(f" ✗ Excepción al apagar pantalla: {e}") + +def shutdown_pc(): + """Apaga el PC en 10 segundos""" + print("4. [COMENTADO] Apagando el PC en 10 segundos...") + print(" ℹ️ Shutdown deshabilitado en modo prueba.") + # os.system("shutdown /s /t 10") + +if __name__ == "__main__": + print("=" * 60) + print(" PROTOCOLO DE APAGADO (TEST - SIN SHUTDOWN)") + print("=" * 60) + cerrar_ventanas() + apagar_luces_alexa() + apagar_pantalla_tapo() + shutdown_pc() + print("=" * 60) + print(" PROTOCOLO COMPLETADO") + print("=" * 60) diff --git a/scripts/test_homeassistant.py b/scripts/test_homeassistant.py new file mode 100644 index 0000000..687b1f9 --- /dev/null +++ b/scripts/test_homeassistant.py @@ -0,0 +1,75 @@ +""" +Script de prueba para verificar control de luces Alexa via Home Assistant. + +Uso: + python test_homeassistant.py [on|off] + +Ejemplos: + python test_homeassistant.py off # Apagar ambas luces + python test_homeassistant.py on # Encender ambas luces +""" +import sys +import os +import requests +from dotenv import load_dotenv + +# Fix encoding para Windows +if sys.platform == 'win32': + import codecs + sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') + sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') + +# Cargar .env +dotenv_path = r"C:\Users\juanm\Documents\GitHub\biblioteca_negocio_prolongo\.env" +load_dotenv(dotenv_path) + +def main(): + url = os.getenv("HOMEASSISTANT_URL") + token = os.getenv("HOMEASSISTANT_TOKEN") + + if not url or not token: + print("[ERROR] Falta HOMEASSISTANT_URL o HOMEASSISTANT_TOKEN en .env") + print() + print("Añade al .env:") + print(" HOMEASSISTANT_URL=http://185.187.169.109:8123") + print(" HOMEASSISTANT_TOKEN=") + return + + # Determinar acción + action = "turn_off" + if len(sys.argv) > 1 and sys.argv[1].lower() == "on": + action = "turn_on" + + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + } + + luces = ["barras", "cuartillo"] + + print("=" * 60) + print(f" TEST HOME ASSISTANT - {action.upper().replace('_', ' ')}") + print("=" * 60) + + for luz in luces: + entity_id = f"light.{luz}" + try: + response = requests.post( + f"{url}/api/services/light/{action}", + headers=headers, + json={"entity_id": entity_id}, + timeout=10 + ) + + if response.status_code == 200: + print(f"[OK] {luz.capitalize()}: {action.replace('_', ' ')}") + else: + print(f"[ERROR] {luz.capitalize()}: HTTP {response.status_code}") + print(f" {response.text}") + except requests.exceptions.RequestException as e: + print(f"[ERROR] {luz.capitalize()}: {e}") + + print("=" * 60) + +if __name__ == "__main__": + main()