import os import time from datetime import datetime import cv2 import config from camara import CamaraRTSP from detector_movimiento import DetectorMovimiento from notificador import (disparar_webhook_alexa, enviar_telegram, enviar_texto_telegram) USAR_YOLO = True identificador = None def notificar_personales(resultados, cam_nombre): """Mensaje personalizado por Telegram a cada destinatario segun la persona.""" conocidas = {r["nombre"] for r in resultados if "nombre" in r} if not conocidas or not config.RECIPIENTES: return nombres = ", ".join(sorted(conocidas)) ts = datetime.now().strftime("%H:%M:%S") for chat in config.RECIPIENTES: for nombre in sorted(conocidas): texto = (f"📢 {nombre}, ¿qué estás haciendo? " f"(vista en {cam_nombre} a las {ts})") enviar_texto_telegram(texto, chat_id=chat) def notificar_discord(resultados, cam_nombre): try: from discord_notif import notificar_discord_personales notificar_discord_personales(resultados, cam_nombre) except Exception as e: print(f"[Discord] Error: {e}") def cargar_identificador(): global identificador if identificador is None: try: from reconocedor import CONOCIDOS, Reconocedor, os as _os if _os.path.isdir(CONOCIDOS) and any( _os.path.isdir(_os.path.join(CONOCIDOS, n)) for n in _os.listdir(CONOCIDOS) ): identificador = Reconocedor() print(f"Identificacion activa. Conocidos: " f"{list(identificador.conocidos.keys())}") else: print("Sin conocidos registrados (ejecuta enroll.py). " "Modo deteccion simple.") except Exception as e: print(f"No se pudo cargar identificador: {e}") return identificador def componer_alerta(resultados, cam_nombre, ts): conocidas = sorted({r["nombre"] for r in resultados if "nombre" in r}) desconocidas = sum(1 for r in resultados if r.get("desconocido")) zona = "autorizada" if cam_nombre in config.ZONAS_AUTORIZADAS else "NO autorizada" sufijo = f" (zona {zona})" if conocidas and desconocidas: return (f"👧 {', '.join(conocidas)} | " f"⚠️ {desconocidas} persona(s) desconocida(s) " f"en {cam_nombre}{sufijo} - {ts}") if conocidas: return (f"👧 {', '.join(conocidas)} detectada(s) " f"en {cam_nombre}{sufijo} - {ts}") if desconocidas: return (f"🚨 {desconocidas} persona(s) en {cam_nombre}" f"{sufijo} - {ts}") return f"🚨 Presencia en {cam_nombre}{sufijo} - {ts}" def detectar_presencia(modelo, frame): """True si hay al menos una persona en el frame (YOLO).""" if modelo is None: return True try: r = modelo.predict(frame, verbose=False, conf=0.4) return any(int(b.cls) == 0 for b in r[0].boxes) except Exception: return True def dentro_de_franja_horaria(hora_actual=None) -> bool: if hora_actual is None: hora_actual = datetime.now().hour inicio = config.HORA_INICIO fin = config.HORA_FIN if inicio < fin: return inicio <= hora_actual < fin return hora_actual >= inicio or hora_actual < fin def main(): if not config.CAMARAS: print("No hay cámaras configuradas en .env") return os.makedirs(config.SNAPSHOT_DIR, exist_ok=True) camaras = [CamaraRTSP(c["rtsp"], c["nombre"]) for c in config.CAMARAS] detectores = [] ultima_alerta = {} for i, c in enumerate(config.CAMARAS): detectores.append(DetectorMovimiento()) ultima_alerta[i] = 0 ident = cargar_identificador() modelo_presencia = None try: from ultralytics import YOLO ruta_yolo = os.path.join(os.path.dirname(os.path.abspath(__file__)), "yolov8n.pt") modelo_presencia = YOLO(ruta_yolo) except Exception as e: print(f"No se pudo cargar YOLO para presencia: {e}") print("Sistema de vigilancia iniciado. Franja activa: " f"{config.HORA_INICIO}:00 - {config.HORA_FIN}:00") while True: if not dentro_de_franja_horaria(): time.sleep(30) continue for i, cam in enumerate(camaras): frame = cam.leer_frame() if frame is None: continue detector = detectores[i] movido = detector.hay_movimiento(frame) ahora = time.time() if not movido: continue if (ahora - ultima_alerta[i]) <= config.COOLDOWN: continue if not detectar_presencia(modelo_presencia, frame): continue ts = datetime.now().strftime("%Y%m%d_%H%M%S") ruta = os.path.join( config.SNAPSHOT_DIR, f"snapshot_{cam.nombre}_{ts}.jpg" ) texto = f"🚨 Presencia en {cam.nombre} - {ts}" resultados = [] if ident is not None: try: for _ in range(3): f2 = cam.leer_frame() if f2 is None: continue resultados += ident.analizar_frame(f2) time.sleep(0.1) if resultados: texto = componer_alerta(resultados, cam.nombre, ts) except Exception as e: print(f"[{cam.nombre}] Error identificando: {e}") else: zona = "autorizada" if cam.nombre in config.ZONAS_AUTORIZADAS else "NO autorizada" texto = f"🚨 Presencia en {cam.nombre} (zona {zona}) - {ts}" cv2_ok = True try: cv2.imwrite(ruta, frame) except Exception as e: cv2_ok = False print(f"[{cam.nombre}] Error guardando snapshot: {e}") if cv2_ok: enviar_telegram(ruta, texto) if resultados: try: notificar_personales(resultados, cam.nombre) notificar_discord(resultados, cam.nombre) except Exception as e: print(f"[{cam.nombre}] Error notificando personales: {e}") disparar_webhook_alexa(texto) ultima_alerta[i] = ahora print(texto) time.sleep(0.5) if __name__ == "__main__": main()