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