# -*- coding: utf-8 -*- """aviso.py — Aviso por voz local (RF-14, RF-15). Emite un aviso sonoro local (reutiliza `scripts/aviso_sonoro.py` del repo de negocio) solo cuando el resumen del ciclo contiene elementos que requieren atencion, o cuando la IA fallo (RF-13). Nunca envia nada fuera del equipo (Regla 5). El runner es inyectable para poder testear sin reproducir audio. """ from __future__ import annotations import subprocess import sys from typing import Callable from . import config import infra_paths def _script_aviso(): """Ruta del script de aviso sonoro (repo de negocio).""" return infra_paths.biblioteca_repo() / "scripts" / "aviso_sonoro.py" def _runner_default(tipo: str) -> None: """Reproduce el aviso sonoro lanzando el script en un subproceso.""" subprocess.run([sys.executable, str(_script_aviso()), tipo], check=False) def avisar(resumen: dict, cfg: dict | None = None, _runner: Callable[[str], None] | None = None) -> bool: """Emite el aviso por voz si procede. Devuelve True si aviso. - Si `aviso.voz` esta desactivado en config -> no hace nada. - Avisa si `fallo_ia` o si hay elementos en `requiere_atencion`. """ cfg = cfg or config.cargar() if not cfg.get("aviso", {}).get("voz", True): return False fallo_ia = bool(resumen.get("fallo_ia")) atencion = bool(resumen.get("requiere_atencion")) if not (fallo_ia or atencion): return False tipo = "fin" if atencion else "error" runner = _runner or _runner_default try: runner(tipo) except Exception: pass return True