10.1. Votre premier point de terminaison

Avant que la caméra puisse faire quoi que ce soit d’intéressant, le reste du réseau doit pouvoir la joindre. La chose la plus simple qui prouve que le serveur est vivant est un point de terminaison HTTP à une seule route qui renvoie du JSON :

from microdot import Microdot

app = Microdot()

frame_count = 0
trigger_count = 0

@app.get('/status')
async def status(request):
    return {'frames': frame_count, 'triggers': trigger_count}

app.run(host='0.0.0.0', port=80)

Exécutez-le dans l’IDE. Depuis n’importe quelle autre machine du réseau local, ouvrez http://<cam-ip>/status. Le navigateur affiche : :

{"frames": 0, "triggers": 0}

Les compteurs sont des valeurs fictives – rien ne les modifie encore – mais la requête a traversé le réseau, la caméra l’a routée, a exécuté un gestionnaire et a renvoyé du JSON.

10.1.1. Ce que fait chaque ligne

Une seule instance microdot.Microdot par script. L’instance détient la table de routage, les gestionnaires d’erreurs et le cycle de vie (démarrage, service, arrêt). Les grosses applications se répartissent en plusieurs modules Python mais partagent toujours un unique objet app.

@app.get('/status') est le décorateur de route. Nous n’utilisons ici que microdot.Microdot.get() ; post(), put() et delete() apparaîtront dans les pages suivantes, lorsque la caméra commencera à accepter des écritures.

Chaque gestionnaire de route est une coroutine asyncio et reçoit la requête comme premier argument. Le gestionnaire n’est pas obligé d’utiliser request – celui-ci l’ignore – mais le paramètre est toujours présent afin que la signature reste cohérente.

Renvoyer un dict est la façon la plus courte d’envoyer du JSON. Microdot sérialise automatiquement le dict en JSON et définit Content-Type: application/json sur la réponse. Renvoyer une chaîne envoie text/plain. Renvoyer explicitement une microdot.Response est la forme longue – nécessaire lorsque le corps est binaire ou lorsque la réponse a besoin d’en-têtes personnalisés.

app.run(host='0.0.0.0', port=80) démarre le serveur. 0.0.0.0 signifie écouter sur chaque interface dont dispose la caméra – à la fois l’ethernet filaire et le STA wifi, si les deux sont actifs. Le port 80 est la valeur par défaut du HTTP, de sorte que les navigateurs n’ont pas besoin de saisir de numéro de port.

10.1.2. Une requête, de bout en bout

Le téléphone ouvre une connexion TCP vers la caméra, envoie une requête HTTP, la caméra l'analyse, la route, exécute le gestionnaire, puis renvoie une réponse.

Le téléphone ouvre une connexion TCP, écrit la ligne de requête et les en-têtes, puis attend. La caméra lit les octets sur le socket, les analyse en un objet microdot.Request, fait correspondre le chemin et la méthode à la table de routage, attend la coroutine du gestionnaire, sérialise ce qu’elle a renvoyé, réécrit une ligne de statut, des en-têtes et un corps sur le socket, puis ferme la connexion (par défaut en HTTP/1.0) ou la recycle (en HTTP/1.1 avec Connection: keep-alive). L’échange complet dure à peu près le temps de l’aller-retour réseau plus ce qu’a fait le gestionnaire.

10.1.3. Une note sur le blocage

run() est bloquant – il ne revient jamais tant que le serveur n’est pas arrêté. C’est acceptable pour un serveur à usage unique. Une application qui capture aussi des trames ou exécute d’autres coroutines utilise plutôt start_server() à l’intérieur d’un asyncio.run(), afin que le serveur HTTP puisse partager la boucle avec tout le reste.

L’application répond à une seule URL.