05-sep: [vigilancia] comandos /aiMesh /qos /limite /seguridad para el ASUS desde Telegram

This commit is contained in:
juanjo
2026-09-05 19:38:03 +02:00
parent e15204a5e8
commit 15f19a7900
7 changed files with 987 additions and 10 deletions

View File

@@ -1,6 +1,7 @@
"""Lectura segura de datos ASUSWRT mediante la API web del router."""
import asyncio
import html
import json
import os
import unicodedata
@@ -8,6 +9,7 @@ import unicodedata
import aiohttp
from asusrouter import AsusData, AsusRouter
from asusrouter.modules.parental_control import PCRuleType, ParentalControlRule
from asusrouter.modules.system import AsusSystem
def _load_env():
@@ -110,3 +112,115 @@ def cambiar_bloqueo(nombre, blocked):
raise ValueError(f"Dispositivo no reconocido: {nombre}")
asyncio.run(_set_block(device["mac"], device["name"], blocked))
return f"{'Bloqueado' if blocked else 'Activado'}: {device['name']}"
async def _aimesh_data():
username = _env("ASUS_USERNAME")
password = _env("ASUS_PASSWORD")
if not username or not password:
raise RuntimeError(
"Falta ASUS_USERNAME o ASUS_PASSWORD en el .env de la Raspberry"
)
session = aiohttp.ClientSession()
router = AsusRouter(
hostname=_env("ASUS_ROUTER_IP", "192.168.50.1"),
username=username,
password=password,
session=session,
)
try:
await router.async_connect()
return await router.async_get_data(AsusData.AIMESH, force=True)
finally:
await router.async_disconnect()
await session.close()
async def _aimesh_action(cmd, mac=None):
username = _env("ASUS_USERNAME")
password = _env("ASUS_PASSWORD")
if not username or not password:
raise RuntimeError(
"Falta ASUS_USERNAME o ASUS_PASSWORD en el .env de la Raspberry"
)
session = aiohttp.ClientSession()
router = AsusRouter(
hostname=_env("ASUS_ROUTER_IP", "192.168.50.1"),
username=username,
password=password,
session=session,
)
try:
await router.async_connect()
if cmd == "reconstruir":
return await router.async_set_state(AsusSystem.AIMESH_REBUILD)
if cmd == "reiniciar_todo":
return await router.async_set_state(AsusSystem.AIMESH_REBOOT)
return await router.async_set_state(
AsusSystem.NODE_REBOOT, device_list=mac
)
finally:
await router.async_disconnect()
await session.close()
def _nodo_texto(node):
tipo = "ROUTER" if getattr(node, "type", "") == "router" else "NODO"
alias = node.alias or node.model or "Sin nombre"
lineas = [f"{tipo} · {alias} ({node.model})"]
estado = "conectado" if node.status else "apagado"
lineas.append(f" estado: {estado}")
if node.ip:
lineas.append(f" ip: {node.ip}")
if node.fw:
lineas.append(f" firmware: {node.fw}")
if node.mac:
lineas.append(f" mac: {node.mac}")
parent = node.parent or {}
if parent:
rssi = parent.get("rssi", "?")
conexion = parent.get("connection", "?")
lineas.append(f" enlace: {conexion} · rssi={rssi}")
for banda, ssid in (node.ap or {}).items():
lineas.append(f" wifi {banda}: {ssid}")
return "\n".join(lineas)
def aimesh_estado():
data = asyncio.run(_aimesh_data()) or {}
if not data:
return "<pre>Sin datos AiMesh</pre>"
nodos = list(data.values())
nodos.sort(key=lambda n: getattr(n, "level", 0))
texto = "\n\n".join(_nodo_texto(n) for n in nodos)
return f"<pre>{html.escape(texto)}</pre>"
def _resolver_nodo(nombre):
data = asyncio.run(_aimesh_data()) or {}
clave = normalizar_nombre(nombre)
for mac, node in data.items():
if mac and mac.lower() == nombre.lower().strip():
return node
alias = normalizar_nombre(node.alias or "")
if clave and (alias == clave or clave in alias or alias in clave):
return node
return None
def aimesh_reiniciar_nodo(nombre):
node = _resolver_nodo(nombre)
if not node:
raise ValueError(f"Nodo AiMesh no encontrado: {nombre}")
asyncio.run(_aimesh_action("nodo", mac=node.mac))
return f"🔄 Nodo reiniciado: {node.alias or node.model} ({node.mac})"
def aimesh_reconstruir():
asyncio.run(_aimesh_action("reconstruir"))
return "🛠️ AiMesh reconstruida. Los nodos se reconectarán en unos minutos."
def aimesh_reiniciar_todo():
asyncio.run(_aimesh_action("reiniciar_todo"))
return "⚠️ Router y todos los nodos se están reiniciando..."