From df1f7f0d938bc9e5074bce4ffdb95937632f6562 Mon Sep 17 00:00:00 2001 From: juanjo Date: Sun, 6 Sep 2026 15:06:20 +0200 Subject: [PATCH] 06-sep: [parental] Bloquear dispositivo: inventario vivo con toggle por numero + alias TV/Xbox --- vigilancia/asus_api_control.py | 85 ++++++++++++++++++++++++++++++++++ vigilancia/menu_idd.py | 57 +++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/vigilancia/asus_api_control.py b/vigilancia/asus_api_control.py index ee4eddd..e9ef282 100644 --- a/vigilancia/asus_api_control.py +++ b/vigilancia/asus_api_control.py @@ -35,8 +35,20 @@ DEVICE_ALIASES = { "carolina": {"name": "Caroline-tablet", "mac": "3A:99:2B:ED:D3:5E"}, "caroline": {"name": "Caroline-tablet", "mac": "3A:99:2B:ED:D3:5E"}, "caroline tablet": {"name": "Caroline-tablet", "mac": "3A:99:2B:ED:D3:5E"}, + "tele": {"name": "Tele", "mac": "14:EA:63:1D:1B:25"}, + "television": {"name": "Tele", "mac": "14:EA:63:1D:1B:25"}, + "tv salon": {"name": "Tele", "mac": "14:EA:63:1D:1B:25"}, + "xbox": {"name": "XBOX", "mac": "A8:8C:3E:7C:F3:C7"}, } +TIPOS = ( + ("📺", ("tele", "tv", "samsung", "lg", "androidtv", "bravia", "philips")), + ("🎮", ("xbox", "playstation", "ps4", "ps5", "nintendo", "switch")), + ("📱", ("iphone", "android", "galaxy", "xiaomi", "redmi", "pixel")), + ("💻", ("pc", "laptop", "minguez", "juanjer", "nahid")), + ("🏠", ("tapo", "dreame", "vacuum", "echo", "alexa", "google", "nest")), +) + def _env(name, default=""): return os.getenv(name, FILE_ENV.get(name, default)).strip() @@ -186,6 +198,79 @@ def fijar_bloqueos(cambios): return _leer_reglas() +def _icono(nombre): + n = normalizar_nombre(nombre) + for icono, claves in TIPOS: + if any(c in n for c in claves): + return icono + return "🔌" + + +def listar_dispositivos(limite=14): + """Inventario vivo: leases DHCP + alias conocidos + estado de bloqueo. + Devuelve lista de dicts (nombre, mac, ip, tipo, bloqueado).""" + try: + from router_control import _inventario, _nombre_por_mac + except Exception: + return [] + try: + devices, _by_mac = _inventario() + except Exception: + devices = [] + try: + reglas = _leer_reglas() + except Exception: + reglas = {} + vistos, salida = set(), [] + for ip, mac, nombre in devices: + if mac in vistos: + continue + vistos.add(mac) + bonito = nombre + try: + bonito = _nombre_por_mac(mac) + except Exception: + pass + if bonito in ("pendiente-identificar", "*"): + bonito = nombre + salida.append({ + "nombre": bonito, "mac": mac, "ip": ip, + "tipo": _icono(f"{bonito} {nombre}"), + "bloqueado": mac in reglas, + }) + for alias, info in DEVICE_ALIASES.items(): + if info["mac"] in vistos: + continue + vistos.add(info["mac"]) + salida.append({ + "nombre": f"{info['name']} (alias)", "mac": info["mac"], + "ip": "", "tipo": _icono(info["name"]), + "bloqueado": info["mac"] in reglas, + }) + orden = {"📺": 0, "🎮": 1, "📱": 2, "💻": 3, "🏠": 4, "🔌": 5} + salida.sort(key=lambda d: (orden.get(d["tipo"], 9), + d["bloqueado"], d["nombre"].lower())) + return salida[:limite] + + +def bloquear_mac(mac, blocked, nombre=""): + """Bloquea/desbloquea por MAC directa (regla 24/7 + iptables + verif).""" + from router_control import bloqueo_red + mac = (mac or "").upper() + if not mac: + raise ValueError("MAC vacia") + fijar_bloqueos([(mac, nombre or mac, blocked)]) + reglas = _leer_reglas() + dentro = mac in reglas if blocked else mac not in reglas + marca = "✅" if dentro else "❌" + base = (f"{'Bloqueado' if blocked else 'Activado'}: " + f"{nombre or mac} ({mac}) {marca}") + try: + return f"{base}\n{bloqueo_red(nombre or mac, mac, blocked)}" + except Exception as e: + return f"{base}\n⚠️ Sin verificacion de red: {e}" + + def cambiar_bloqueo(nombre, blocked): device = resolver_dispositivo(nombre) if not device: diff --git a/vigilancia/menu_idd.py b/vigilancia/menu_idd.py index 23ce37d..d1147a4 100644 --- a/vigilancia/menu_idd.py +++ b/vigilancia/menu_idd.py @@ -185,6 +185,7 @@ def teclado_parental(): _btn("🔓 Desbloquear tablets", PREFIJO + "acc:tablets:desbloquear"), ], [_btn("📋 Listar estado", PREFIJO + "acc:parental_listar")], + [_btn("📺 Bloquear dispositivo…", PREFIJO + "acc:dispositivos")], [ _btn("🔒 Desactivar…", PREFIJO + "sub:desactivar"), _btn("🔓 Activar…", PREFIJO + "sub:activar"), @@ -258,6 +259,39 @@ def teclado_minutos_pausa(nombre): ]) +# Snapshot de la lista de dispositivos por chat (para toggle por numero). +menu_dispositivos = {} + + +def teclado_dispositivos(lista): + filas = [] + for i, d in enumerate(lista, start=1): + marca = "🔒" if d["bloqueado"] else "🔓" + extra = f" {d['ip']}" if d.get("ip") else "" + filas.append([_btn(f"{i}. {marca} {d['tipo']} {d['nombre'][:28]}{extra}", + PREFIJO + f"acc:blqdev:{i}")]) + filas.append([_btn("⬅️ Control parental", PREFIJO + "cat:parental")]) + return _mk(filas) + + +def _mostrar_dispositivos(chat_id, message_id): + import pi_bot + try: + from asus_api_control import listar_dispositivos + lista = listar_dispositivos() + except Exception as e: + _enviar_nuevo(chat_id, f"❌ Error listando dispositivos: {e}", None) + return + if not lista: + _enviar_nuevo(chat_id, "No hay dispositivos en red.", None) + return + menu_dispositivos[chat_id] = lista + _enviar_edit(chat_id, message_id, + "📺 Bloquear dispositivo\nPulsa el número para " + "bloquear/desbloquear:", + teclado_dispositivos(lista)) + + def teclado_resultados(lista, prefijo): filas = [] for i, item in enumerate(lista[:12], start=1): @@ -612,6 +646,29 @@ def _ejecutar_accion(chat_id, message_id, partes): _cerrar_menu(chat_id, message_id) _fin_accion(chat_id, "aspi") return + if accion == "dispositivos": + _record_cat(chat_id, "parental") + _mostrar_dispositivos(chat_id, message_id) + return + if accion == "blqdev": + _record_cat(chat_id, "parental") + lista = menu_dispositivos.get(chat_id) or [] + numero = int(sub) if sub.isdigit() else 0 + if 1 <= numero <= len(lista): + d = lista[numero - 1] + try: + from asus_api_control import bloquear_mac, listar_dispositivos + bloquear_mac(d["mac"], not d["bloqueado"], d["nombre"]) + lista = listar_dispositivos() + menu_dispositivos[chat_id] = lista + except Exception as e: + _enviar_nuevo(chat_id, f"❌ Error: {e}", None) + return + _enviar_edit(chat_id, message_id, + "📺 Bloquear dispositivo\nPulsa el número para " + "bloquear/desbloquear:", + teclado_dispositivos(lista)) + return if accion == "tablets": if sub in ("bloquear", "desbloquear"): verbo = "desactivar" if sub == "bloquear" else "activar"