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:
@@ -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", "")
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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">🖨</span>
|
||||||
|
<span id="printer-erreur-msg">Problème imprimante</span>
|
||||||
|
<button onclick="document.getElementById('printer-erreur').classList.add('cache')">✕</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">📷</div>
|
<div class="camera-erreur-icon">📷</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()">⚠ Évacuer / Réinitialiser imprimante</button>
|
<button class="btn-danger" onclick="evacuerBourrage()">⚠ Annuler jobs</button>
|
||||||
|
<button class="btn-secondaire" onclick="resetUsb()">↻ 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>
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user