05-sep: [router] integrar ASUS en Telegram
This commit is contained in:
@@ -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.
|
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
|
## Despliegue
|
||||||
|
|
||||||
La Pi no tiene Git operativo para este flujo. El despliegue se hace por el VPS y el tunel inverso:
|
La Pi no tiene Git operativo para este flujo. El despliegue se hace por el VPS y el tunel inverso:
|
||||||
|
|||||||
@@ -40,3 +40,10 @@
|
|||||||
- **Resultado:** Implementacion desplegada; pendiente de credenciales Tapo locales.
|
- **Resultado:** Implementacion desplegada; pendiente de credenciales Tapo locales.
|
||||||
- **Decisiones:** Comandos permitidos: `/termo on`, `/termo off` y `/termo estado`.
|
- **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.
|
- **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.
|
||||||
|
|||||||
@@ -383,6 +383,52 @@ def procesar(texto, chat_id):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"termo error: {e}")
|
log(f"termo error: {e}")
|
||||||
enviar(f"❌ Error controlando el termo: {e}", chat_id)
|
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(
|
||||||
|
"<b>ROUTER ASUS</b>\n"
|
||||||
|
"<code>/router estado</code> — modelo, firmware, IP y uptime\n"
|
||||||
|
"<code>/router clientes</code> — tabla ARP de clientes\n"
|
||||||
|
"<code>/router wifi</code> — radios Wi-Fi\n"
|
||||||
|
"<code>/router reiniciar confirmar</code> — reinicio protegido",
|
||||||
|
chat_id,
|
||||||
|
)
|
||||||
|
elif accion_router in ("estado", "status"):
|
||||||
|
try:
|
||||||
|
from router_control import estado
|
||||||
|
enviar("📡 <b>ASUS</b>\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("📶 <b>CLIENTES ASUS</b>\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("📡 <b>WIFI ASUS</b>\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: <code>/router reiniciar confirmar</code>", 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 <code>/router ayuda</code>.", chat_id)
|
||||||
elif t in ("pelis", "/pelis"):
|
elif t in ("pelis", "/pelis"):
|
||||||
procesar_pelis(chat_id)
|
procesar_pelis(chat_id)
|
||||||
elif t in ("series", "/series"):
|
elif t in ("series", "/series"):
|
||||||
@@ -442,15 +488,17 @@ def procesar(texto, chat_id):
|
|||||||
" ✏️ Ej: <code>/estado</code>\n"
|
" ✏️ Ej: <code>/estado</code>\n"
|
||||||
"<b>14.</b> <code>temperatura</code> — temperatura de la Pi\n"
|
"<b>14.</b> <code>temperatura</code> — temperatura de la Pi\n"
|
||||||
" ✏️ Ej: <code>/temperatura</code>\n"
|
" ✏️ Ej: <code>/temperatura</code>\n"
|
||||||
"<b>15.</b> <code>panorama</code> — fotos de las cámaras\n"
|
"<b>15.</b> <code>router estado|clientes|wifi</code> — controla el ASUS\n"
|
||||||
|
" ✏️ Ej: <code>/router estado</code>\n"
|
||||||
|
"<b>16.</b> <code>panorama</code> — fotos de las cámaras\n"
|
||||||
" ✏️ Ej: <code>/panorama</code>\n"
|
" ✏️ Ej: <code>/panorama</code>\n"
|
||||||
"<b>16.</b> <code>limpieza</code> — limpia procesos atascados\n"
|
"<b>17.</b> <code>limpieza</code> — limpia procesos atascados\n"
|
||||||
" ✏️ Ej: <code>/limpieza</code>\n"
|
" ✏️ Ej: <code>/limpieza</code>\n"
|
||||||
"<b>17.</b> <code>servicios</code> — estado de los servicios\n"
|
"<b>18.</b> <code>servicios</code> — estado de los servicios\n"
|
||||||
" ✏️ Ej: <code>/servicios</code>\n"
|
" ✏️ Ej: <code>/servicios</code>\n"
|
||||||
"<b>18.</b> <code>reboot</code> — reinicia la Pi\n"
|
"<b>19.</b> <code>reboot</code> — reinicia la Pi\n"
|
||||||
" ✏️ Ej: <code>/reboot</code> → confirmar con <code>/confirmar-reboot</code>\n"
|
" ✏️ Ej: <code>/reboot</code> → confirmar con <code>/confirmar-reboot</code>\n"
|
||||||
"<b>19.</b> <code>ayuda</code> — este índice\n"
|
"<b>20.</b> <code>ayuda</code> — este índice\n"
|
||||||
" ✏️ Ej: <code>/ayuda</code>",
|
" ✏️ Ej: <code>/ayuda</code>",
|
||||||
chat_id)
|
chat_id)
|
||||||
elif t in ("servicios", "/servicios"):
|
elif t in ("servicios", "/servicios"):
|
||||||
|
|||||||
69
vigilancia/router_control.py
Normal file
69
vigilancia/router_control.py
Normal file
@@ -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"<pre>{html.escape(output)}</pre>"
|
||||||
|
|
||||||
|
|
||||||
|
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"<pre>{html.escape(output)}</pre>"
|
||||||
|
|
||||||
|
|
||||||
|
def clientes():
|
||||||
|
output = _run("cat /proc/net/arp")
|
||||||
|
return f"<pre>{html.escape(output)}</pre>"
|
||||||
|
|
||||||
|
|
||||||
|
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)())
|
||||||
Reference in New Issue
Block a user