Documentation technique

DOCUMENTATION TECHNIQUE

Architecture, stack technique, structure du projet et base de données. La conception du système SmartLocker, telle que décrite dans le TFE.

Architecture générale

Quatre couches indépendantes communiquant entre elles

L'architecture de SmartLocker repose sur quatre couches distinctes qui communiquent de façon indépendante : le smartphone (ou le navigateur de l'utilisateur), l'application Flask hébergée sur PythonAnywhere, la base de données Firebase Realtime, et l'infrastructure matérielle — un Raspberry Pi 4B en passerelle et deux Raspberry Pi Zero W équipant respectivement le casier École et le casier Sport, reliés par MQTT.

Topologie réseau du système SmartLocker
Figure 8 : Topologie réseau du système SmartLocker (adresses IP illustratives)

Flux d'ouverture d'un casier

  1. L'utilisateur clique sur « Ouvrir » dans l'interface web.
  2. Flask met à jour le champ status / cmd_open_door dans Firebase.
  3. La passerelle (Pi 4B) détecte le changement par polling toutes les secondes.
  4. Elle publie la commande sur le topic MQTT locker/SPORT_01/commande.
  5. Le Pi Zero reçoit la commande, active le relais 4 secondes, puis publie l'état réel de la porte sur locker/SPORT_01/etat.
  6. La passerelle réceptionne cet état et le réécrit dans Firebase.
  7. L'interface web affiche la mise à jour en temps réel.

Stack technique

Choix technologiques et justifications

Flask — Backend

Micro-framework web Python, léger et flexible, adapté à un projet de taille moyenne. Il n'impose pas de structure rigide et autorise l'ajout progressif des fonctionnalités. Django (trop lourd) et Node.js/Express ont été écartés.

Firebase Realtime DB

Stocke les données en JSON et synchronise tous les clients en quelques millisecondes. Essentiel pour que l'état physique de la porte soit visible quasi instantanément. Firebase Auth gère le login (email + Google OAuth) sans stocker les mots de passe.

MQTT — Communication matériel

Protocole de messagerie léger pour objets connectés (modèle publish/subscribe). Découple le casier de la connexion Internet et garantit la livraison (QoS 1). Paho-MQTT sur les Pi Zero, broker Mosquitto sur le Pi 4B.

Stripe Checkout — Paiement

API très bien documentée, mode test avec cartes fictives, page de paiement préconstruite. Les métadonnées de la session (date, durée, utilisateur) permettent d'appliquer la réservation côté serveur de manière idempotente.

Firebase Cloud Messaging

Notifications push sur smartphone même app fermée. Lors d'une alerte d'intrusion, la passerelle transmet une notification via FCM à l'utilisateur concerné ; le Service Worker de la PWA l'affiche.

PythonAnywhere — Hébergement

Hébergement Python/Flask avec domaine en pythonanywhere.com, certificat HTTPS automatique et interface d'administration simple. L'offre gratuite suffit pour la démonstration du TFE.

Comparaison des solutions

SmartLocker face aux alternatives existantes

Critère Casier classique Solution pro App mobile SmartLocker
CoûtTrès faibleTrès élevéMoyenFaible
TraçabilitéNonOuiPartielleOui
Accessible webNonPartielleNonOui
Paiement intégréNonOuiOuiOui
Alerte intrusionNonOuiNonOui
Open sourceN/ANonNonOui
Matériel abordableOuiNonNonOui

Structure du projet

Organisation selon les conventions de Flask

smartlocker/ ├── flask_app.py # Point d'entrée : crée l'app + enregistre les blueprints ├── extensions.py # Connexions partagées (Firebase, Stripe) ├── helpers.py # Fonctions utilitaires + décorateurs d'accès ├── routes_main.py # Dashboard, scan, settings, PWA ├── routes_auth.py # Login, signup, logout, Google ├── routes_pairing.py # Appairage / dissociation des casiers ├── routes_sport.py # Casier Sport (Premium) ├── routes_school.py # Casier École (réservation Stripe + webhook) ├── routes_api.py # API JSON, notifications, historique ├── routes_admin.py # Console d'administration ├── config.py # Clés API, config Firebase et Stripe ├── templates/ # Pages HTML (Jinja2) │ ├── index.html # Tableau de bord principal │ ├── locker_school.html │ ├── locker_sport.html │ ├── history.html │ ├── settings.html │ ├── admin.html │ ├── scan.html │ ├── payment_success.html │ └── sport_thanks.html ├── static/ │ ├── service-worker.js │ ├── manifest.json │ └── firebase-messaging-sw.js └── app.css

Base de données Firebase

Structure de la Realtime Database

La Realtime Database s'articule autour de plusieurs arbres principaux : lockers (état des casiers), users (profils, historique et tokens FCM), lockers_index (index inverse casier → propriétaire) et gateways (état de la passerelle). Le schéma sépare clairement les états des commandes.

smartlocker-94e11-rtdb/ ├── lockers/ │ ├── SPORT_01/ │ │ ├── status : 'locked' | 'unlocked' | 'reserved' │ │ ├── active_session : { user_id, started_at, expires_at, amount_eur } │ │ ├── door_physical : 'Ouverte' | 'Fermee' | 'Inconnu' │ │ ├── cmd_open_door : timestamp (impulsion d'ouverture) │ │ ├── online_status : 'online' | 'offline' │ │ ├── lockdown_until : timestamp │ │ ├── alert_active : string │ │ ├── distance_cm : number │ │ └── content_state : 'occupied' | 'empty' │ └── ECOLE_01/ (mêmes champs + stripe_session_id pour l'idempotence) ├── users/ │ └── {uid}/ │ ├── pseudo, email, photoURL │ ├── premium : bool │ ├── fcm_token : string │ ├── paired : { 'sport': true } | { 'school': true } │ └── history/{pushId} : { locker_key, started_at, ended_at, ... } ├── lockers_index/ { 'sport': uid, 'school': uid } (index O(1)) └── gateways/pi4b/ { status, last_seen }

Topics MQTT

Communication entre la passerelle (Pi 4B) et les casiers (Pi Zero)

TopicDirectionContenu
locker/SPORT_01/commandePi 4B → Pi Zero« ouvrir » | « reset » | « etat »
locker/SPORT_01/etatPi Zero → Pi 4B« Ouverte » | « Fermée » | « commande_recue »
locker/SPORT_01/alertePi Zero → Pi 4B« intrusion » | « lockdown_actif » | « lockdown_reset »
locker/SPORT_01/presencePi Zero → Pi 4BDistance en cm (float)
locker/SPORT_01/statusPi Zero → Pi 4B« en_ligne|timestamp|lockdown=non|alertes=0 »

Pour le détail complet de la conception et de la réalisation, consultez la rédaction du TFE ou parcourez le code source.