diff --git a/backend/main.py b/backend/main.py index 55625c9..691ef6a 100644 --- a/backend/main.py +++ b/backend/main.py @@ -505,15 +505,65 @@ async def api_imprimantes(): return lister_imprimantes() +@app.get("/api/imprimante/statut-detail") +async def api_statut_imprimante(): + """Retourne le statut détaillé CUPS + dernière erreur du log.""" + import subprocess + config = charger_config() + nom = config.get("impression", {}).get("imprimante", "Mitsubishi") + # Statut lpstat + r = subprocess.run(["lpstat", "-p", nom], capture_output=True, text=True) + statut_ligne = r.stdout.strip() + # Dernière erreur dans error_log + try: + r2 = subprocess.run( + ["sudo", "grep", f"\\[{nom}\\]\\|Job.*cancel\\|media.*match\\|jam\\|paper", + "/var/log/cups/error_log"], + capture_output=True, text=True + ) + lignes = [l for l in r2.stdout.splitlines() if "error_log" not in l] + derniere_erreur = lignes[-1] if lignes else "" + except Exception: + derniere_erreur = "" + return { + "statut": statut_ligne, + "derniere_erreur": derniere_erreur, + "imprimante": nom, + } + + @app.post("/api/imprimante/evacuer") async def api_evacuer_bourrage(): - """Annule tous les jobs et réactive l'imprimante pour débloquer un bourrage.""" + """Annule tous les jobs, réactive l'imprimante, retourne le dernier message d'erreur.""" import subprocess config = charger_config() nom = config.get("impression", {}).get("imprimante", "Mitsubishi") subprocess.run(["sudo", "cancel", "-a", nom], capture_output=True) subprocess.run(["sudo", "cupsenable", nom], capture_output=True) - return {"succes": True, "message": f"Jobs annulés, {nom} réactivée"} + # Lire la dernière erreur CUPS pour informer l'utilisateur + try: + r = subprocess.run( + ["sudo", "tail", "-50", "/var/log/cups/error_log"], + capture_output=True, text=True + ) + erreurs = [l for l in r.stdout.splitlines() + if any(k in l.lower() for k in ["media", "match", "jam", "paper", "cancel", "error"]) + and "createprofile" not in l.lower()] + derniere = erreurs[-1] if erreurs else "" + except Exception: + derniere = "" + + if "media does not match" in derniere.lower(): + msg = ("Format papier incorrect — vérifiez que le format configuré " + "correspond à la cassette dans l'imprimante") + elif "jam" in derniere.lower(): + msg = "Bourrage papier — retirez le papier bloqué physiquement" + elif derniere: + msg = f"Jobs annulés. Dernière erreur : {derniere.split(']')[-1].strip()}" + else: + msg = f"Jobs annulés, {nom} réactivée" + + return {"succes": True, "message": msg} @app.post("/api/imprimer") @@ -800,8 +850,23 @@ async def _lancer_photostation(): # Fermer Chromium (la boucle kiosk attend le flag avant de le relancer) await asyncio.create_subprocess_exec("pkill", "chromium") await asyncio.sleep(2) + import subprocess as _sp env = {**__import__("os").environ, "DISPLAY": ":0"} - cmd = f"source {PHOTOSTATION_DIR}/venv/bin/activate && python {PHOTOSTATION_DIR}/src/main.py --kiosk" + # Récupérer les variables de session X/dbus depuis la session LightDM + try: + r = _sp.run(["grep", "-z", "DISPLAY\|XAUTHORITY\|DBUS_SESSION", + f"/proc/{__import__('os').getpid()}/environ"], + capture_output=True) + for kv in r.stdout.split(b"\x00"): + if b"=" in kv: + k, v = kv.decode(errors="ignore").split("=", 1) + if k in ("DISPLAY", "XAUTHORITY", "DBUS_SESSION_BUS_ADDRESS"): + env[k] = v + except Exception: + pass + log_path = "/tmp/photostation.log" + cmd = (f"source {PHOTOSTATION_DIR}/venv/bin/activate && " + f"python {PHOTOSTATION_DIR}/src/main.py --kiosk >> {log_path} 2>&1") proc = await asyncio.create_subprocess_exec("bash", "-c", cmd, env=env) await proc.wait() FLAG_PHOTOSTATION.unlink(missing_ok=True) diff --git a/frontend/index.html b/frontend/index.html index b5d681e..fe8c736 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -396,7 +396,10 @@