diff --git a/vigilancia/menu_idd.py b/vigilancia/menu_idd.py index d1147a4..4b94136 100644 --- a/vigilancia/menu_idd.py +++ b/vigilancia/menu_idd.py @@ -116,6 +116,9 @@ def teclado_domotica(): _btn("πŸ€– Aspiradora", PREFIJO + "cat:aspi"), _btn("πŸ”Œ Termo", PREFIJO + "cat:termo"), ], + [ + _btn("πŸ–₯️ Pantalla", PREFIJO + "cat:pantalla"), + ], FILA_VOLVER, ]) @@ -167,6 +170,17 @@ def teclado_termo(): ]) +def teclado_pantalla(): + return _mk([ + [ + _btn("πŸ–₯️ ON", PREFIJO + "acc:pantalla:on"), + _btn("πŸ–₯️ OFF", PREFIJO + "acc:pantalla:off"), + _btn("πŸ“Š Estado", PREFIJO + "acc:pantalla:estado"), + ], + [_btn("⬅️ DomΓ³tica", PREFIJO + "cat:domotica")], + ]) + + def teclado_tv(): return _mk([ [ @@ -409,6 +423,7 @@ def _header(categoria): "aspi": "πŸ€– Aspiradora", "potencia": "⚑ Potencia aspiradora", "termo": "πŸ”Œ Termo", + "pantalla": "πŸ–₯️ Pantalla", "sistema": "πŸ› οΈ Sistema", "router": "πŸ“‘ Router ASUS", } @@ -453,6 +468,7 @@ def _teclado_categoria(chat_id, categoria): "aspi": teclado_aspi, "potencia": teclado_potencia, "termo": teclado_termo, + "pantalla": teclado_pantalla, "sistema": teclado_sistema, "router": teclado_router, } @@ -624,13 +640,13 @@ def _ejecutar_accion(chat_id, message_id, partes): if accion in directos: _disp(chat_id, message_id, directos[accion], CAT_ACC.get(accion)) return - if accion == "termo": + if accion in ("termo", "pantalla"): if sub == "estado": - _reabrir(chat_id, message_id, "termo", "termo estado") + _reabrir(chat_id, message_id, accion, f"{accion} estado") elif sub in ("on", "off"): - pi_bot.procesar(f"termo {sub}", chat_id) + pi_bot.procesar(f"{accion} {sub}", chat_id) _cerrar_menu(chat_id, message_id) - _fin_accion(chat_id, "termo") + _fin_accion(chat_id, accion) return if accion == "aspiradora": if sub in ("estado", "stats", "consumibles"): diff --git a/vigilancia/pi_bot.py b/vigilancia/pi_bot.py index ca82982..1af8e1b 100644 --- a/vigilancia/pi_bot.py +++ b/vigilancia/pi_bot.py @@ -516,20 +516,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": + elif comando in ("termo", "pantalla"): accion = argumento.lower().strip() acciones = {"on": "on", "encender": "on", "off": "off", "apagar": "off", "estado": "status", "status": "status"} if accion not in acciones: - enviar("Uso: /termo on, /termo off o /termo estado", chat_id) + enviar(f"Uso: /{comando} on, /{comando} off o /{comando} estado", chat_id) else: try: - from tapo_control import controlar_termo - resultado = controlar_termo(acciones[accion]) + from tapo_control import controlar + resultado = controlar(comando, acciones[accion]) enviar(f"πŸ”Œ {resultado}", chat_id) except Exception as e: - log(f"termo error: {e}") - enviar(f"❌ Error controlando el termo: {e}", chat_id) + log(f"{comando} error: {e}") + enviar(f"❌ Error controlando {comando}: {e}", chat_id) elif comando == "router": partes_router = argumento.lower().split() accion_router = partes_router[0] if partes_router else "ayuda" @@ -809,7 +809,9 @@ def procesar(texto, chat_id): "9. descargar <enlace> β€” aΓ±ade a Download Station\n" " ✏️ Ej: /descargar magnet:?xt=urn:btih:...\n" "10. termo on|off|estado β€” controla el enchufe Tapo\n" - " ✏️ Ej: /termo estado\n\n" + " ✏️ Ej: /termo estado\n" + "10c. pantalla on|off|estado β€” enchufe de la pantalla\n" + " ✏️ Ej: /pantalla estado\n\n" "━━━ 🏠 DOMΓ“TICA ━━━\n" "10b. aspiradora ... β€” Dreame D10 Plus\n" " ✏️ Ej: /aspiradora estado, /aspiradora vaciar, /aspiradora stats, /aspiradora potencia 3, /aspiradora agua 2, /aspiradora voz\n\n" diff --git a/vigilancia/tapo_control.py b/vigilancia/tapo_control.py index 53b7d18..6cc2d3b 100644 --- a/vigilancia/tapo_control.py +++ b/vigilancia/tapo_control.py @@ -28,21 +28,28 @@ def _env(name): return os.getenv(name, FILE_ENV.get(name, "")).strip() -async def _device(): - host = _env("TAPO_TERMO_IP") +DISPOSITIVOS = { + "termo": "TAPO_TERMO_IP", + "pantalla": "TAPO_PANTALLA_IP", +} + + +async def _device(nombre="termo"): + var_ip = DISPOSITIVOS.get(nombre, "TAPO_TERMO_IP") + host = _env(var_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" + f"Falta configurar {var_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() +async def _run(nombre, action): + device = await _device(nombre) await device.update() if action == "on": await device.turn_on() @@ -52,18 +59,23 @@ async def _run(action): raise ValueError("Accion no soportada") await device.update() state = "encendido" if device.is_on else "apagado" - return f"termo: {state}" + return f"{nombre}: {state}" -def controlar_termo(action): +def controlar(nombre, action): """Ejecuta una accion Tapo y devuelve un mensaje seguro para Telegram.""" try: - return asyncio.run(_run(action)) + return asyncio.run(_run(nombre, 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]}" + return f"No se pudo controlar {nombre}: {message[:180]}" + + +def controlar_termo(action): + """Compatibilidad: el termo es el dispositivo por defecto.""" + return controlar("termo", action) async def _discover():