diff --git a/docs/raspberry_pi.md b/docs/raspberry_pi.md index 8b459c8..ac4a26c 100644 --- a/docs/raspberry_pi.md +++ b/docs/raspberry_pi.md @@ -69,6 +69,33 @@ ssh -p 2222 usuario_del_router@127.0.0.1 El script mantiene el salto PC -> VPS -> Raspberry -> ASUS sin exponer el router a Internet. +Desde Telegram, el bot consulta el ASUS mediante la clave de servicio de la Raspberry: + +```text +/router estado +/router clientes +/router wifi +/router reiniciar confirmar +``` + +El reinicio exige la palabra `confirmar`. Las consultas actuales no cambian la configuracion. + +## Capacidades del RT-AX88U PRO + +El router puede centralizar muchas funciones para Telegram: + +- Estado del firmware, uptime, IP LAN/WAN y radios Wi-Fi. +- Clientes conectados y tabla ARP. +- Estado de Internet y rutas. +- Control de radios 2.4 GHz y 5 GHz. +- Reservas DHCP y nombres de dispositivos. +- Reinicio protegido y lectura de logs. +- Estado AiMesh, si se añaden nodos. +- QoS, control parental y perfiles de acceso. +- VPN, DDNS, firewall y red de invitados. + +Las acciones que cambian red, Wi-Fi, firewall o VPN deben tener confirmacion doble. + ## Despliegue La Pi no tiene Git operativo para este flujo. El despliegue se hace por el VPS y el tunel inverso: diff --git a/documentacion/bitacora-dashboard-telegram-2026-09-05.md b/documentacion/bitacora-dashboard-telegram-2026-09-05.md index 4ecaf05..de3aef0 100644 --- a/documentacion/bitacora-dashboard-telegram-2026-09-05.md +++ b/documentacion/bitacora-dashboard-telegram-2026-09-05.md @@ -40,3 +40,10 @@ - **Resultado:** Implementacion desplegada; pendiente de credenciales Tapo locales. - **Decisiones:** Comandos permitidos: `/termo on`, `/termo off` y `/termo estado`. - **Gotchas/Lecciones:** La cuenta Tapo y su contrasena no se guardan en el repositorio; deben introducirse directamente en el `.env` de la Pi. + +### [18:25] Control Telegram del ASUS +- **Que:** Integrar consultas y acciones protegidas del router ASUS desde Telegram. +- **Como:** `router_control.py` usa la clave SSH de servicio de la Raspberry y una lista cerrada de comandos. +- **Resultado:** OK; estado, clientes y Wi-Fi verificados desde la Pi. +- **Decisiones:** El reinicio exige `/router reiniciar confirmar`; no se habilitan comandos arbitrarios. +- **Gotchas/Lecciones:** El ASUS esta detras del router Digi; el acceso se hace siempre por Raspberry/VPS y no se expone SSH a Internet. diff --git a/vigilancia/pi_bot.py b/vigilancia/pi_bot.py index 37bf824..d88c613 100644 --- a/vigilancia/pi_bot.py +++ b/vigilancia/pi_bot.py @@ -383,6 +383,52 @@ def procesar(texto, chat_id): except Exception as e: log(f"termo error: {e}") enviar(f"❌ Error controlando el termo: {e}", chat_id) + elif comando == "router": + partes_router = argumento.lower().split() + accion_router = partes_router[0] if partes_router else "ayuda" + if accion_router in ("ayuda", "help"): + enviar( + "ROUTER ASUS\n" + "/router estado — modelo, firmware, IP y uptime\n" + "/router clientes — tabla ARP de clientes\n" + "/router wifi — radios Wi-Fi\n" + "/router reiniciar confirmar — reinicio protegido", + chat_id, + ) + elif accion_router in ("estado", "status"): + try: + from router_control import estado + enviar("📡 ASUS\n" + estado(), chat_id) + except Exception as e: + log(f"router estado error: {e}") + enviar(f"❌ Error consultando ASUS: {e}", chat_id) + elif accion_router in ("clientes", "clients"): + try: + from router_control import clientes + enviar("📶 CLIENTES ASUS\n" + clientes(), chat_id) + except Exception as e: + log(f"router clientes error: {e}") + enviar(f"❌ Error consultando clientes: {e}", chat_id) + elif accion_router == "wifi": + try: + from router_control import wifi + enviar("📡 WIFI ASUS\n" + wifi(), chat_id) + except Exception as e: + log(f"router wifi error: {e}") + enviar(f"❌ Error consultando Wi-Fi: {e}", chat_id) + elif accion_router in ("reiniciar", "reboot"): + if len(partes_router) < 2 or partes_router[1] != "confirmar": + enviar("Para reiniciar responde exactamente: /router reiniciar confirmar", chat_id) + else: + try: + from router_control import reiniciar + enviar("⚠️ Reiniciando el ASUS...", chat_id) + reiniciar() + except Exception as e: + log(f"router reinicio error: {e}") + enviar(f"❌ Error reiniciando ASUS: {e}", chat_id) + else: + enviar("Acción de router no reconocida. Usa /router ayuda.", chat_id) elif t in ("pelis", "/pelis"): procesar_pelis(chat_id) elif t in ("series", "/series"): @@ -442,15 +488,17 @@ def procesar(texto, chat_id): " ✏️ Ej: /estado\n" "14. temperatura — temperatura de la Pi\n" " ✏️ Ej: /temperatura\n" - "15. panorama — fotos de las cámaras\n" + "15. router estado|clientes|wifi — controla el ASUS\n" + " ✏️ Ej: /router estado\n" + "16. panorama — fotos de las cámaras\n" " ✏️ Ej: /panorama\n" - "16. limpieza — limpia procesos atascados\n" + "17. limpieza — limpia procesos atascados\n" " ✏️ Ej: /limpieza\n" - "17. servicios — estado de los servicios\n" + "18. servicios — estado de los servicios\n" " ✏️ Ej: /servicios\n" - "18. reboot — reinicia la Pi\n" + "19. reboot — reinicia la Pi\n" " ✏️ Ej: /reboot → confirmar con /confirmar-reboot\n" - "19. ayuda — este índice\n" + "20. ayuda — este índice\n" " ✏️ Ej: /ayuda", chat_id) elif t in ("servicios", "/servicios"): diff --git a/vigilancia/router_control.py b/vigilancia/router_control.py new file mode 100644 index 0000000..e207d09 --- /dev/null +++ b/vigilancia/router_control.py @@ -0,0 +1,69 @@ +"""Consultas seguras al ASUS RT-AX88U PRO desde la Raspberry.""" + +import html +import subprocess +import sys + + +ROUTER = "jjminguez@192.168.50.1" +KEY = "/home/pi/.ssh/router_control" +SSH_BASE = [ + "ssh", + "-i", KEY, + "-o", "BatchMode=yes", + "-o", "ConnectTimeout=8", + ROUTER, +] + + +def _run(command): + try: + result = subprocess.run( + SSH_BASE + [command], + capture_output=True, + text=True, + timeout=15, + ) + except Exception as exc: + return f"Error conectando al router: {exc}" + if result.returncode != 0: + return f"Error del router: {result.stderr.strip()[-300:]}" + return result.stdout.strip() + + +def estado(): + output = _run( + "printf '%s\\n' " + "'Modelo='$(nvram get productid) " + "'Firmware='$(nvram get firmver).$(nvram get buildno) " + "'LAN='$(nvram get lan_ipaddr) " + "'WAN='$(nvram get wan0_ipaddr) " + "'WiFi24='$(nvram get wl0_radio) " + "'WiFi5='$(nvram get wl1_radio); uptime" + ) + return f"
{html.escape(output)}
" + + +def wifi(): + output = _run( + "printf '%s\\n' " + "'2.4GHz='$(nvram get wl0_radio) " + "'5GHz='$(nvram get wl1_radio) " + "'6GHz='$(nvram get wl2_radio)" + ) + return f"
{html.escape(output)}
" + + +def clientes(): + output = _run("cat /proc/net/arp") + return f"
{html.escape(output)}
" + + +def reiniciar(): + return _run("reboot") + + +if __name__ == "__main__": + action = sys.argv[1] if len(sys.argv) > 1 else "estado" + actions = {"estado": estado, "clientes": clientes, "wifi": wifi} + print(actions.get(action, estado)())