Watchdog WiFi + reconnexion auto + fix timing surprise capture
- Watchdog connectivite toutes les 60s (check TCP 1.1.1.1:53) - Si offline : scan WiFi et reconnexion auto aux reseaux enregistres - Si internet restaure : flush immediat des spools (email + galerie) - Surprise : capture declenchee 500ms apres affichage (au lieu de 1.7s apres) pour capturer la reaction naturelle des gens Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -179,15 +179,13 @@ def envoyer_rapport_spool(nb_envoyes: int, destinataires: list[str], nb_echoues:
|
|||||||
|
|
||||||
|
|
||||||
RETRY_INTERVAL = 300 # 5 minutes
|
RETRY_INTERVAL = 300 # 5 minutes
|
||||||
|
WATCHDOG_INTERVAL = 60 # 1 minute
|
||||||
|
|
||||||
|
|
||||||
async def tache_spool_periodique():
|
async def _flush_spools():
|
||||||
"""Retente emails + galerie en spool toutes les 5 min."""
|
"""Vide les spools email + galerie."""
|
||||||
from backend.destinations import traiter_booth_spool, taille_booth_spool
|
from backend.destinations import traiter_booth_spool, taille_booth_spool
|
||||||
await asyncio.sleep(15)
|
|
||||||
while True:
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
# Emails
|
|
||||||
total_mail = taille_spool()
|
total_mail = taille_spool()
|
||||||
if total_mail > 0:
|
if total_mail > 0:
|
||||||
log.info(f"Spool email: retry ({total_mail} en attente)")
|
log.info(f"Spool email: retry ({total_mail} en attente)")
|
||||||
@@ -195,12 +193,32 @@ async def tache_spool_periodique():
|
|||||||
if nb > 0:
|
if nb > 0:
|
||||||
echoues = total_mail - nb
|
echoues = total_mail - nb
|
||||||
await loop.run_in_executor(None, envoyer_rapport_spool, nb, dests, echoues)
|
await loop.run_in_executor(None, envoyer_rapport_spool, nb, dests, echoues)
|
||||||
# Galerie booth
|
|
||||||
total_booth = taille_booth_spool()
|
total_booth = taille_booth_spool()
|
||||||
if total_booth > 0:
|
if total_booth > 0:
|
||||||
log.info(f"Spool galerie: retry ({total_booth} en attente)")
|
log.info(f"Spool galerie: retry ({total_booth} en attente)")
|
||||||
await loop.run_in_executor(None, traiter_booth_spool)
|
await loop.run_in_executor(None, traiter_booth_spool)
|
||||||
await asyncio.sleep(RETRY_INTERVAL)
|
|
||||||
|
|
||||||
|
async def tache_spool_periodique():
|
||||||
|
"""Watchdog connectivite (60s) + retry spool (5 min)."""
|
||||||
|
from backend.wifi import watchdog_tick
|
||||||
|
from backend.destinations import taille_booth_spool
|
||||||
|
await asyncio.sleep(15)
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
ticks_depuis_flush = 0
|
||||||
|
while True:
|
||||||
|
result = await loop.run_in_executor(None, watchdog_tick)
|
||||||
|
if result == "restored":
|
||||||
|
await _flush_spools()
|
||||||
|
ticks_depuis_flush = 0
|
||||||
|
else:
|
||||||
|
ticks_depuis_flush += 1
|
||||||
|
if ticks_depuis_flush >= RETRY_INTERVAL // WATCHDOG_INTERVAL:
|
||||||
|
has_spool = taille_spool() > 0 or taille_booth_spool() > 0
|
||||||
|
if has_spool:
|
||||||
|
await _flush_spools()
|
||||||
|
ticks_depuis_flush = 0
|
||||||
|
await asyncio.sleep(WATCHDOG_INTERVAL)
|
||||||
|
|
||||||
|
|
||||||
async def tache_spool_demarrage():
|
async def tache_spool_demarrage():
|
||||||
|
|||||||
@@ -201,3 +201,62 @@ def wifi_forget(ssid: str) -> dict:
|
|||||||
|
|
||||||
def wifi_get_password(ssid: str) -> str | None:
|
def wifi_get_password(ssid: str) -> str | None:
|
||||||
return _charger_mdp().get(ssid)
|
return _charger_mdp().get(ssid)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Watchdog connectivité ---
|
||||||
|
|
||||||
|
_internet_ok = False
|
||||||
|
|
||||||
|
|
||||||
|
def check_internet(timeout: int = 5) -> bool:
|
||||||
|
"""Teste la connectivité internet (DNS + HTTP rapide)."""
|
||||||
|
import socket
|
||||||
|
try:
|
||||||
|
socket.create_connection(("1.1.1.1", 53), timeout=timeout).close()
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def watchdog_tick() -> str | None:
|
||||||
|
"""Vérifie internet. Retourne 'restored' si passage offline→online, None sinon."""
|
||||||
|
global _internet_ok
|
||||||
|
now_ok = check_internet()
|
||||||
|
if now_ok and not _internet_ok:
|
||||||
|
_internet_ok = True
|
||||||
|
log.info("Internet restauré — flush spool")
|
||||||
|
return "restored"
|
||||||
|
_internet_ok = now_ok
|
||||||
|
if not now_ok:
|
||||||
|
_tenter_reconnexion_wifi()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _tenter_reconnexion_wifi():
|
||||||
|
"""Si déconnecté du WiFi, tente de se reconnecter à un réseau enregistré visible."""
|
||||||
|
status = wifi_status()
|
||||||
|
if status["connecte"]:
|
||||||
|
return
|
||||||
|
log.info("Pas de WiFi — scan des réseaux enregistrés")
|
||||||
|
saved = {n["ssid"] for n in wifi_saved_list()}
|
||||||
|
if not saved:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
r = subprocess.run(
|
||||||
|
["nmcli", "-t", "-f", "SSID,SIGNAL", "dev", "wifi", "list", "--rescan", "yes"],
|
||||||
|
capture_output=True, text=True, timeout=15,
|
||||||
|
)
|
||||||
|
candidates = []
|
||||||
|
for line in r.stdout.strip().split("\n"):
|
||||||
|
p = _nmcli_split(line)
|
||||||
|
if len(p) >= 2 and p[0] in saved and p[1].isdigit():
|
||||||
|
candidates.append((p[0], int(p[1])))
|
||||||
|
candidates.sort(key=lambda x: -x[1])
|
||||||
|
for ssid, sig in candidates:
|
||||||
|
log.info(f"Tentative reconnexion WiFi: {ssid} (signal {sig}%)")
|
||||||
|
result = wifi_connect(ssid)
|
||||||
|
if result.get("succes"):
|
||||||
|
log.info(f"Reconnecté à {ssid}")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
log.warning(f"Reconnexion WiFi échouée: {e}")
|
||||||
|
|||||||
@@ -1146,7 +1146,7 @@
|
|||||||
|
|
||||||
<script src="/js/websocket.js?v=16"></script>
|
<script src="/js/websocket.js?v=16"></script>
|
||||||
<script src="/js/app.js?v=25"></script>
|
<script src="/js/app.js?v=25"></script>
|
||||||
<script src="/js/camera.js?v=23"></script>
|
<script src="/js/camera.js?v=24"></script>
|
||||||
<script src="/js/effects.js?v=4"></script>
|
<script src="/js/effects.js?v=4"></script>
|
||||||
<script src="/js/gallery.js?v=5"></script>
|
<script src="/js/gallery.js?v=5"></script>
|
||||||
<script src="/js/share.js?v=10"></script>
|
<script src="/js/share.js?v=10"></script>
|
||||||
|
|||||||
@@ -22,13 +22,12 @@ function prechargerSurprise() {
|
|||||||
_surprisePreloaded = img;
|
_surprisePreloaded = img;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function afficherSurprise() {
|
function montrerSurprise() {
|
||||||
const surprise = (config || {}).surprise;
|
const surprise = (config || {}).surprise;
|
||||||
if (!surprise || !surprise.actif || !surprise.fichier) return;
|
if (!surprise || !surprise.actif || !surprise.fichier) return false;
|
||||||
const delai = surprise.delai_ms || 1500;
|
|
||||||
|
|
||||||
const overlay = document.getElementById('surprise-overlay');
|
const overlay = document.getElementById('surprise-overlay');
|
||||||
if (!overlay) return;
|
if (!overlay) return false;
|
||||||
|
|
||||||
if (surprise.type === 'video') {
|
if (surprise.type === 'video') {
|
||||||
overlay.innerHTML = '<video src="/api/surprise/media" autoplay muted playsinline style="max-width:100%;max-height:100%;object-fit:contain"></video>';
|
overlay.innerHTML = '<video src="/api/surprise/media" autoplay muted playsinline style="max-width:100%;max-height:100%;object-fit:contain"></video>';
|
||||||
@@ -39,26 +38,24 @@ async function afficherSurprise() {
|
|||||||
overlay.appendChild(_surprisePreloaded);
|
overlay.appendChild(_surprisePreloaded);
|
||||||
} else {
|
} else {
|
||||||
overlay.innerHTML = '<img src="/api/surprise/media" style="max-width:100%;max-height:100%;object-fit:contain">';
|
overlay.innerHTML = '<img src="/api/surprise/media" style="max-width:100%;max-height:100%;object-fit:contain">';
|
||||||
const img = overlay.querySelector('img');
|
|
||||||
if (img && !img.complete) {
|
|
||||||
await new Promise(r => { img.onload = r; img.onerror = r; setTimeout(r, 500); });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
overlay.style.opacity = '0';
|
overlay.style.opacity = '0';
|
||||||
overlay.classList.remove('cache');
|
overlay.classList.remove('cache');
|
||||||
overlay.style.transition = 'opacity 0.2s';
|
overlay.style.transition = 'opacity 0.15s';
|
||||||
overlay.style.opacity = '1';
|
overlay.style.opacity = '1';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
await new Promise(r => setTimeout(r, delai));
|
function cacherSurprise() {
|
||||||
|
const overlay = document.getElementById('surprise-overlay');
|
||||||
|
if (!overlay) return;
|
||||||
overlay.style.opacity = '0';
|
overlay.style.opacity = '0';
|
||||||
await new Promise(r => setTimeout(r, 200));
|
setTimeout(() => {
|
||||||
overlay.classList.add('cache');
|
overlay.classList.add('cache');
|
||||||
overlay.style.transition = '';
|
overlay.style.transition = '';
|
||||||
overlay.style.opacity = '';
|
overlay.style.opacity = '';
|
||||||
|
}, 200);
|
||||||
prechargerSurprise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Preview live ---
|
// --- Preview live ---
|
||||||
@@ -320,7 +317,9 @@ async function lancerCapture() {
|
|||||||
await compteARebours(avecPreMessages);
|
await compteARebours(avecPreMessages);
|
||||||
|
|
||||||
// Surprise : seulement en photo simple (pas strip/multi)
|
// Surprise : seulement en photo simple (pas strip/multi)
|
||||||
if (modeActuel === 'simple') await afficherSurprise();
|
// Affiche la surprise PUIS capture pendant que les gens reagissent
|
||||||
|
const aSurprise = (modeActuel === 'simple') && montrerSurprise();
|
||||||
|
if (aSurprise) await pause(500);
|
||||||
|
|
||||||
// Flash blanc immédiat + lancer capture en parallèle
|
// Flash blanc immédiat + lancer capture en parallèle
|
||||||
const flash = document.getElementById('flash-blanc');
|
const flash = document.getElementById('flash-blanc');
|
||||||
@@ -331,6 +330,7 @@ async function lancerCapture() {
|
|||||||
|
|
||||||
arreterPreview();
|
arreterPreview();
|
||||||
let resultat = await apiPost('/api/capturer').catch(() => null);
|
let resultat = await apiPost('/api/capturer').catch(() => null);
|
||||||
|
if (aSurprise) { cacherSurprise(); prechargerSurprise(); }
|
||||||
|
|
||||||
if (!resultat && !resultat?.erreur) {
|
if (!resultat && !resultat?.erreur) {
|
||||||
flash.classList.add('cache');
|
flash.classList.add('cache');
|
||||||
|
|||||||
Reference in New Issue
Block a user