20-sep: Home Assistant en VPS para control luces Alexa (gratis)

This commit is contained in:
juanminsanz
2026-09-20 21:12:35 +02:00
parent aab08f5fce
commit dff7262ebd
8 changed files with 864 additions and 0 deletions

View 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()

View 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()

View 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 ===")

View 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)

View 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)

View 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()