diff --git a/docs/raspberry_pi.md b/docs/raspberry_pi.md index a9588b0..1f4a751 100644 --- a/docs/raspberry_pi.md +++ b/docs/raspberry_pi.md @@ -150,6 +150,8 @@ Reservas aplicadas para los 39 leases actuales y 3 reservas historicas conservad Control parental por dispositivo o grupo: horarios, bloqueo temporal, perfiles, pausas desde Telegram y registro de cambios. Se implementara despues de terminar la normalizacion de nombres del inventario. +La lectura avanzada de control parental usa `asusrouter` y requiere `ASUS_USERNAME` y `ASUS_PASSWORD` solo en el `.env` local de la Raspberry. No se guardan en el repositorio. + ## Despliegue La Pi no tiene Git operativo para este flujo. El despliegue se hace por el VPS y el tunel inverso: diff --git a/vigilancia/.env.example b/vigilancia/.env.example index 7643793..dfc9be8 100644 --- a/vigilancia/.env.example +++ b/vigilancia/.env.example @@ -13,6 +13,11 @@ TAPO_USERNAME= TAPO_PASSWORD= TAPO_TERMO_IP= +# ASUSWRT API para control parental (no compartir la contrasena) +ASUS_ROUTER_IP=192.168.50.1 +ASUS_USERNAME= +ASUS_PASSWORD= + # Franja horaria activa (formato 24h) HORA_INICIO=08 HORA_FIN=17 diff --git a/vigilancia/asus_api_control.py b/vigilancia/asus_api_control.py new file mode 100644 index 0000000..fff22ab --- /dev/null +++ b/vigilancia/asus_api_control.py @@ -0,0 +1,57 @@ +"""Lectura segura de datos ASUSWRT mediante la API web del router.""" + +import asyncio +import json +import os + +import aiohttp +from asusrouter import AsusData, AsusRouter + + +def _load_env(): + values = {} + path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env") + try: + with open(path) as stream: + for line in stream: + line = line.strip() + if line and not line.startswith("#") and "=" in line: + key, value = line.split("=", 1) + values[key.strip()] = value.strip() + except OSError: + pass + return values + + +FILE_ENV = _load_env() + + +def _env(name, default=""): + return os.getenv(name, FILE_ENV.get(name, default)).strip() + + +async def _parental_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.PARENTAL_CONTROL, force=True) + finally: + await router.async_disconnect() + await session.close() + + +def parental_status(): + data = asyncio.run(_parental_data()) + return json.dumps(data, ensure_ascii=False, indent=2, default=str) diff --git a/vigilancia/router_control.py b/vigilancia/router_control.py index 2f7796d..a123a9a 100644 --- a/vigilancia/router_control.py +++ b/vigilancia/router_control.py @@ -115,6 +115,30 @@ def reservas(): return f"
{html.escape(output)}"
+def control_parental():
+ try:
+ from asus_api_control import parental_status
+ return f"{html.escape(parental_status())}"
+ except RuntimeError as exc:
+ return f"{html.escape(str(exc))}"
+ except Exception:
+ pass
+ keys = (
+ "yadns_enable_x",
+ "yadns_mode",
+ "yadns_clientlist",
+ "bwdpi_enable",
+ "bwdpi_rulelist",
+ "keyword_rulelist",
+ "url_rulelist",
+ "time_zone",
+ )
+ lines = []
+ for key in keys:
+ lines.append(f"{key}={_run(f'nvram get {key}')}")
+ return f"{html.escape(chr(10).join(lines))}"
+
+
def reiniciar():
return _run("reboot")
@@ -128,5 +152,6 @@ if __name__ == "__main__":
"inventario": inventario,
"reservar": reservar_dhcp,
"reservas": reservas,
+ "parental": control_parental,
}
print(actions.get(action, estado)())