Imprimante : bandeau erreur tous écrans + reset USB + statut détaillé

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 21:17:27 +02:00
parent 2b204ddb5e
commit 843762b6d7
5 changed files with 103 additions and 2 deletions

View File

@@ -566,6 +566,38 @@ async def api_evacuer_bourrage():
return {"succes": True, "message": msg} return {"succes": True, "message": msg}
@app.post("/api/imprimante/reset-usb")
async def api_reset_usb():
"""Réinitialise la connexion USB de l'imprimante (unbind/rebind driver)."""
import subprocess, glob
try:
# Trouver le device USB Mitsubishi K60 (VID 06d3)
devs = glob.glob("/sys/bus/usb/devices/*/idVendor")
path = None
for f in devs:
if open(f).read().strip() == "06d3":
path = f.replace("/idVendor", "")
break
if not path:
return {"succes": False, "message": "Imprimante USB non trouvée — vérifiez la connexion"}
dev_id = path.split("/")[-1]
# Annuler les jobs d'abord
nom = charger_config().get("impression", {}).get("imprimante", "Mitsubishi")
subprocess.run(["sudo", "cancel", "-a", nom], capture_output=True)
# Unbind/rebind USB
subprocess.run(["sudo", "sh", "-c", f"echo '{dev_id}' > /sys/bus/usb/drivers/usb/unbind"],
capture_output=True)
await asyncio.sleep(1)
subprocess.run(["sudo", "sh", "-c", f"echo '{dev_id}' > /sys/bus/usb/drivers/usb/bind"],
capture_output=True)
await asyncio.sleep(2)
subprocess.run(["sudo", "cupsenable", nom], capture_output=True)
return {"succes": True, "message": "Imprimante réinitialisée — prête dans quelques secondes"}
except Exception as e:
return {"succes": False, "message": str(e)}
@app.post("/api/imprimer") @app.post("/api/imprimer")
async def api_imprimer(donnees: dict): async def api_imprimer(donnees: dict):
nom = donnees.get("photo", "") nom = donnees.get("photo", "")

View File

@@ -1533,6 +1533,29 @@ h3 {
} }
/* === Erreur camera === */ /* === Erreur camera === */
.printer-erreur {
position: fixed;
bottom: 0; left: 0; right: 0;
background: #b71c1c;
color: #fff;
display: flex;
align-items: center;
gap: 1rem;
padding: 0.8rem 1.5rem;
z-index: 9999;
font-size: 1rem;
}
.printer-erreur-icon { font-size: 1.4rem; flex-shrink: 0; }
.printer-erreur span:nth-child(2) { flex: 1; }
.printer-erreur button {
background: none; border: 2px solid rgba(255,255,255,0.5);
color: #fff; border-radius: 50%; width: 32px; height: 32px;
cursor: pointer; font-size: 1rem; flex-shrink: 0;
}
.camera-erreur { .camera-erreur {
position: fixed; position: fixed;
top: 0; left: 0; top: 0; left: 0;

View File

@@ -13,6 +13,13 @@
<body> <body>
<div id="app"> <div id="app">
<!-- Bandeau erreur imprimante (tous écrans) -->
<div id="printer-erreur" class="printer-erreur cache">
<span class="printer-erreur-icon">&#128424;</span>
<span id="printer-erreur-msg">Problème imprimante</span>
<button onclick="document.getElementById('printer-erreur').classList.add('cache')">&#10005;</button>
</div>
<!-- Overlay erreur camera --> <!-- Overlay erreur camera -->
<div id="camera-erreur" class="camera-erreur cache"> <div id="camera-erreur" class="camera-erreur cache">
<div class="camera-erreur-icon">&#128247;</div> <div class="camera-erreur-icon">&#128247;</div>
@@ -395,8 +402,9 @@
<input type="number" id="admin-copies-max" min="1" max="20" value="5"> <input type="number" id="admin-copies-max" min="1" max="20" value="5">
</div> </div>
<button class="btn-action" onclick="sauvegarderMateriel()">Sauvegarder</button> <button class="btn-action" onclick="sauvegarderMateriel()">Sauvegarder</button>
<div class="champ" style="margin-top:1rem"> <div class="champ" style="margin-top:1rem;display:flex;gap:0.8rem;flex-wrap:wrap">
<button class="btn-danger" onclick="evacuerBourrage()">&#9888; Évacuer / Réinitialiser imprimante</button> <button class="btn-danger" onclick="evacuerBourrage()">&#9888; Annuler jobs</button>
<button class="btn-secondaire" onclick="resetUsb()">&#8635; Réinit. USB</button>
</div> </div>
<div id="imprimante-statut-detail" class="champ" style="display:none"> <div id="imprimante-statut-detail" class="champ" style="display:none">
<div id="imprimante-erreur-msg" style="background:rgba(244,67,54,0.1);border-radius:8px;padding:0.8rem;color:#f44336;font-size:0.85rem;margin-top:0.5rem"></div> <div id="imprimante-erreur-msg" style="background:rgba(244,67,54,0.1);border-radius:8px;padding:0.8rem;color:#f44336;font-size:0.85rem;margin-top:0.5rem"></div>

View File

@@ -92,6 +92,12 @@ function _getOrientations() {
return { ...existing, [fmt]: orient }; return { ...existing, [fmt]: orient };
} }
async function resetUsb() {
afficherStatut('Réinitialisation USB...', 'succes');
const r = await apiPost('/api/imprimante/reset-usb', {});
afficherStatut(r.message, r.succes ? 'succes' : 'erreur');
}
async function evacuerBourrage() { async function evacuerBourrage() {
const r = await apiPost('/api/imprimante/evacuer', {}); const r = await apiPost('/api/imprimante/evacuer', {});
afficherStatut(r.message || 'Imprimante réactivée', r.succes ? 'succes' : 'erreur'); afficherStatut(r.message || 'Imprimante réactivée', r.succes ? 'succes' : 'erreur');

View File

@@ -373,6 +373,38 @@ wsOnMessage('camera_ok', () => {
document.getElementById('camera-erreur').classList.add('cache'); document.getElementById('camera-erreur').classList.add('cache');
}); });
// Surveillance imprimante — poll toutes les 30s
function _afficherErreurImprimante(msg) {
const el = document.getElementById('printer-erreur');
document.getElementById('printer-erreur-msg').textContent = msg;
el.classList.remove('cache');
}
async function _pollStatutImprimante() {
try {
const r = await apiGet('/api/imprimante/statut-detail');
const el = document.getElementById('printer-erreur');
if (r.derniere_erreur) {
let msg = '';
const e = r.derniere_erreur.toLowerCase();
if (e.includes('media') && e.includes('match'))
msg = '⚠ Imprimante : format papier incorrect — vérifiez la cassette';
else if (e.includes('jam'))
msg = '⚠ Imprimante : bourrage papier — retirez le papier bloqué';
else if (e.includes('cancel'))
msg = '⚠ Imprimante : job annulé — ' + r.derniere_erreur.split(']').pop().trim();
if (msg) { _afficherErreurImprimante(msg); return; }
}
if (r.statut && (r.statut.includes('stopped') || r.statut.includes('disabled'))) {
_afficherErreurImprimante('⚠ Imprimante arrêtée — utilisez le bouton Évacuer dans l\'admin');
} else {
el.classList.add('cache');
}
} catch(e) {}
}
setInterval(_pollStatutImprimante, 30000);
// === WIZARD === // === WIZARD ===
let wizardStep = 1; let wizardStep = 1;