20-sep: Home Assistant en VPS para control luces Alexa (gratis)
This commit is contained in:
62
docs/configuracion-voicemonkey-luces.md
Normal file
62
docs/configuracion-voicemonkey-luces.md
Normal file
@@ -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=<tu-token-de-voicemonkey>
|
||||
VOICEMONKEY_DEVICE=<nombre-del-echo>
|
||||
```
|
||||
|
||||
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
|
||||
217
docs/home-assistant-setup-alexa.md
Normal file
217
docs/home-assistant-setup-alexa.md
Normal file
@@ -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=<token-copiado>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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 <token>"}
|
||||
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/
|
||||
85
scripts/configurar_voicemonkey.py
Normal file
85
scripts/configurar_voicemonkey.py
Normal file
@@ -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=<tu-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 "<elige tu Echo>"))
|
||||
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 "<elige tu Echo>"))
|
||||
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()
|
||||
98
scripts/monitor_homeassistant.py
Normal file
98
scripts/monitor_homeassistant.py
Normal file
@@ -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()
|
||||
75
scripts/protocolo_apagado.py
Normal file
75
scripts/protocolo_apagado.py
Normal file
@@ -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 ===")
|
||||
126
scripts/protocolo_apagado_homeassistant.py
Normal file
126
scripts/protocolo_apagado_homeassistant.py
Normal file
@@ -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)
|
||||
126
scripts/protocolo_apagado_test.py
Normal file
126
scripts/protocolo_apagado_test.py
Normal file
@@ -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)
|
||||
75
scripts/test_homeassistant.py
Normal file
75
scripts/test_homeassistant.py
Normal file
@@ -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=<tu-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()
|
||||
Reference in New Issue
Block a user