Architecture
Le projet est composé de quatre parties applicatives principales, complétées par TimescaleDB/PostgreSQL, Redis, Mosquitto, Grafana et Nginx dans la stack Docker locale.
Backend Django
Le dossier backend/ contient la configuration Django, les modèles, les
serializers, les vues API et les commandes de bootstrap. L’API REST est exposée
sous /api/ et utilise des jetons JWT. Le refresh token navigateur est placé
dans un cookie HttpOnly; le frontend garde seulement le jeton d’accès en
mémoire et le renouvelle via /api/token/refresh/.
Frontend Vite
Le dossier frontend/ contient une application Vite multi-pages. Chaque page
HTML charge un module d’entrée sous frontend/src/pages/ qui regroupe le
client API commun et le script métier de la page.
En développement, le service Docker frontend expose le serveur Vite sur le
port 5173. Les pages consomment l’API Django exposée sur 8000.
Le frontend est aussi installable comme PWA. Le manifeste et le service worker
sont servis depuis frontend/public/. Ils donnent une expérience mobile proche
d’une application native sans créer un client séparé : l’utilisateur peut garder
le dashboard sur l’écran d’accueil de son téléphone, tandis que les données
métier restent récupérées en ligne depuis l’API.
Réservation
Le flux utilisateur de réservation sélectionne une station sur une carte, puis un début et une durée de recharge. L’utilisateur ne choisit pas directement l’emplacement : le backend vérifie les bornes et emplacements disponibles sur la fenêtre demandée, puis attribue automatiquement un emplacement libre au moment de la création. Les durées proposées sont 30 minutes, 1 heure, 2 heures et 4 heures, avec 1 heure par défaut.
Les emplacements restent des objets métier côté backend. Ils portent la réservation effective, les sessions de charge et le rattachement aux devices ESP32.
L’infrastructure est structurée en trois niveaux : Station pour le lieu,
Borne pour l’armoire ou le kiosque physique, puis Emplacement pour le
point de charge réellement réservé. Le champ station reste disponible sur
l’emplacement pour les filtres rapides, mais il est dérivé de la borne afin de
conserver la hiérarchie terrain.
Moyens d’accès
Le backend distingue les badges RFID persistants des accès temporaires. Les badges RFID sont rattachés au compte utilisateur et peuvent être gérés depuis le profil ou depuis l’écran administrateur de modification d’un utilisateur. La valeur complète du badge n’est pas exposée dans l’administration.
Les QR codes et codes SMS temporaires sont rattachés à une réservation. Ils peuvent
être régénérés ou révoqués depuis l’administration des réservations, tandis que
le terminal refuse les moyens d’accès expirés ou révoqués. La régénération admin
remplace la valeur côté serveur sans afficher le secret dans une fenêtre de copie;
l’endpoint admin ne renvoie pas la valeur régénérée. L’interface utilisateur
peut ensuite rafraîchir le QR courant ou l’état d’envoi du code SMS, sans exposer
le secret SMS en clair. Les
actions sensibles sont journalisées dans JournalSecurite.
Kiosque Raspberry
Le dossier raspberry/app/ contient une interface locale simple exposée sur le
port 9000 dans la stack Docker.
L’écran tactile cible une résolution de 1024x600. Les parcours critiques du
kiosque doivent être validés sur cette taille, notamment l’accueil, la lecture
RFID, la saisie code et l’association badge.
Le serveur Django reste le chemin nominal : il valide les réservations, les moyens d’accès et la station attendue avant toute ouverture de charge. Le Raspberry intervient comme interface de borne et comme relais local de commande vers les ESP32.
Un mode dégradé est prévu pour maintenir une continuité minimale si le serveur central ou la liaison applicative devient indisponible. Dans ce cas, le Raspberry peut s’appuyer sur un cache local de droits déjà synchronisés, strictement limité dans le temps, pour autoriser uniquement les actions prévues sur les emplacements de la station et de la borne concernées. Les ESP32 conservent de leur côté les événements et télémétries non transmis dans un journal local, par exemple via LittleFS, puis les republient lorsque la communication revient.
Ce choix sépare clairement l’autorité métier et l’exécution terrain. Le backend reste la source de vérité pour les réservations, les révocations, l’audit et la cohérence globale. Le Raspberry, lui, exécute localement les commandes déjà validées et conserve une continuité limitée grâce à son cache offline. Les ESP32 ne prennent jamais de décision d’accès : ils reçoivent des ordres locaux via MQTT, appliquent les sécurités physiques et publient leurs états.
Reverse proxy local
Le dossier nginx/ contient une configuration de reverse proxy locale qui
centralise les routes d’accès :
/vers le frontend Vite ;/api/et/admin/vers Django ;/grafana/vers Grafana ;/kiosk/vers le kiosque Raspberry.
Cette configuration prépare la mise en production en isolant progressivement les
services internes derrière un point d’entrée unique. En développement, Nginx
écoute 80 et 443 : le trafic HTTP est redirigé vers HTTPS et le
certificat auto-signé local permet de valider les cookies sécurisés, les routes
proxy et les en-têtes X-Forwarded-Proto dans des conditions proches d’un
déploiement réel. Les ports directs restent disponibles pour le diagnostic.
ESP32
Le dossier esp32/ contient la logique embarquée de référence pour un
emplacement de recharge. En configuration locale, le backend crée
trois emplacements et associe un device ESP32 à chacun d’eux.
En production, le broker Mosquitto est installé localement sur le Raspberry de
la borne. Les ESP32, la simulation Wokwi ou le service Docker
mqtt-simulator publient les mesures sur le canal
station/<slot_id>/telemetry. Les ESP32 publient également leurs événements
d’état sur station/<slot_id>/status et leurs alertes explicites sur
station/<slot_id>/alert. Le service mqtt-bridge écoute ces canaux quand
la liaison serveur est disponible, délègue le traitement à Celery, puis le
backend stocke les échantillons dans CapteurData et met à jour
Device.last_seen. Cette chaîne alimente l’interface de supervision et le
dashboard Grafana en quasi temps réel.
La supervision distingue la connexion de l’équipement et son dernier état métier
reçu. La connexion est calculée avec Device.last_seen : un ESP32 sans
télémétrie ni statut MQTT récent est considéré hors ligne, même si son dernier
status stocké vaut online. Le champ status reste une information
métier historisée, par exemple charging ou error.
Les alertes opérationnelles sont créées depuis la télémétrie : intrusion,
câble retiré, surchauffe, surconsommation et état critique. Une alerte démarre
au statut nouvelle, passe à active si l’anomalie continue, puis à
resolue quand une télémétrie normale confirme le retour à l’état attendu.
Les ESP32 doivent aussi pouvoir réagir localement sans attendre le serveur pour les sécurités physiques : arrêt de charge, déverrouillage contrôlé, émission d’un événement d’alerte et journalisation locale. Le stockage embarqué ne remplace pas la base centrale ; il évite la perte de traces pendant une coupure temporaire.
Cache Redis
Le service Docker redis expose un cache partagé sur le port 6379. Django
l’utilise quand CACHE_URL est défini, notamment pour le catalogue des
stations. Sans CACHE_URL, le backend revient à un cache local en mémoire.