05-sep: [tapo] controlar termo desde Telegram

This commit is contained in:
juanjo
2026-09-05 16:47:41 +02:00
parent 4d99d5a24d
commit d897e662d3
5 changed files with 125 additions and 1 deletions

View File

@@ -8,6 +8,11 @@ CAM_2_RTSP=rtsp://usuario:password@192.168.1.51:554/stream2
TELEGRAM_TOKEN=123456789:AAExampleTokenAqui
TELEGRAM_CHAT_ID=987654321
# Tapo directo desde la Raspberry (no compartir la contrasena)
TAPO_USERNAME=
TAPO_PASSWORD=
TAPO_TERMO_IP=
# Franja horaria activa (formato 24h)
HORA_INICIO=08
HORA_FIN=17

View File

@@ -369,6 +369,20 @@ def procesar(texto, chat_id):
except Exception as e:
log(f"descargar error: {e}")
enviar(f"❌ Error en Download Station: {e}", chat_id)
elif comando == "termo":
accion = argumento.lower().strip()
acciones = {"on": "on", "encender": "on", "off": "off",
"apagar": "off", "estado": "status", "status": "status"}
if accion not in acciones:
enviar("Uso: <code>/termo on</code>, <code>/termo off</code> o <code>/termo estado</code>", chat_id)
else:
try:
from tapo_control import controlar_termo
resultado = controlar_termo(acciones[accion])
enviar(f"🔌 <b>{resultado}</b>", chat_id)
except Exception as e:
log(f"termo error: {e}")
enviar(f"❌ Error controlando el termo: {e}", chat_id)
elif t in ("pelis", "/pelis"):
procesar_pelis(chat_id)
elif t in ("series", "/series"):
@@ -413,7 +427,9 @@ def procesar(texto, chat_id):
"<b>8.</b> <code>descargas</code> — panel con progreso y botones\n"
" ✏️ Ej: <code>/descargas</code> o <code>/descargar</code>\n"
"<b>9.</b> <code>descargar &lt;enlace&gt;</code> — añade a Download Station\n"
" ✏️ Ej: <code>/descargar magnet:?xt=urn:btih:...</code>\n\n"
" ✏️ Ej: <code>/descargar magnet:?xt=urn:btih:...</code>\n"
"<b>10.</b> <code>termo on|off|estado</code> — controla el enchufe Tapo\n"
" ✏️ Ej: <code>/termo estado</code>\n\n"
"━━━ 📺 TV ━━━\n"
"<b>10.</b> <code>tv &lt;mensaje&gt;</code> — mensaje rápido a la tele\n"
" ✏️ Ej: <code>/tv la cena está lista</code>\n"

View File

@@ -0,0 +1,76 @@
"""Control directo de dispositivos Tapo desde la Raspberry Pi."""
import asyncio
import os
from kasa import Discover
def _load_env():
values = {}
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
try:
with open(path) as stream:
for line in stream:
line = line.strip()
if line and not line.startswith("#") and "=" in line:
key, value = line.split("=", 1)
values[key.strip()] = value.strip()
except OSError:
pass
return values
FILE_ENV = _load_env()
def _env(name):
return os.getenv(name, FILE_ENV.get(name, "")).strip()
async def _device():
host = _env("TAPO_TERMO_IP")
username = _env("TAPO_USERNAME")
password = _env("TAPO_PASSWORD")
if not host or not username or not password:
raise RuntimeError(
"Falta configurar TAPO_TERMO_IP, TAPO_USERNAME y TAPO_PASSWORD en .env"
)
return await Discover.discover_single(
host, username=username, password=password, timeout=8
)
async def _run(action):
device = await _device()
await device.update()
if action == "on":
await device.turn_on()
elif action == "off":
await device.turn_off()
elif action != "status":
raise ValueError("Accion no soportada")
await device.update()
state = "encendido" if device.is_on else "apagado"
return f"termo: {state}"
def controlar_termo(action):
"""Ejecuta una accion Tapo y devuelve un mensaje seguro para Telegram."""
try:
return asyncio.run(_run(action))
except Exception as exc:
message = str(exc)
if "TAPO_" in message:
return f"Error de configuracion Tapo: {message}"
return f"No se pudo controlar el termo: {message[:180]}"
async def _discover():
devices = await Discover.discover(timeout=5)
for device in devices.values():
print(f"{type(device).__name__} host={device.host!r} mac={device.mac}")
if __name__ == "__main__":
asyncio.run(_discover())