{"id":22180192,"url":"https://github.com/bts-lasalle-avignon-ressources/api-http-rest","last_synced_at":"2026-04-18T06:08:15.135Z","repository":{"id":224192136,"uuid":"759332725","full_name":"bts-lasalle-avignon-ressources/api-http-rest","owner":"bts-lasalle-avignon-ressources","description":"Les API HTTP REST (OpenAPI, Esp32 \u0026 Raspberry Pi)","archived":false,"fork":false,"pushed_at":"2024-02-24T10:32:42.000Z","size":4192,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-24T18:50:26.147Z","etag":null,"topics":["api-rest","esp32","openapi","raspberrypi"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bts-lasalle-avignon-ressources.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-02-18T09:49:10.000Z","updated_at":"2024-02-24T10:34:04.000Z","dependencies_parsed_at":"2024-02-24T12:42:00.455Z","dependency_job_id":null,"html_url":"https://github.com/bts-lasalle-avignon-ressources/api-http-rest","commit_stats":null,"previous_names":["bts-lasalle-avignon-ressources/api-http-rest"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bts-lasalle-avignon-ressources/api-http-rest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bts-lasalle-avignon-ressources%2Fapi-http-rest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bts-lasalle-avignon-ressources%2Fapi-http-rest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bts-lasalle-avignon-ressources%2Fapi-http-rest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bts-lasalle-avignon-ressources%2Fapi-http-rest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bts-lasalle-avignon-ressources","download_url":"https://codeload.github.com/bts-lasalle-avignon-ressources/api-http-rest/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bts-lasalle-avignon-ressources%2Fapi-http-rest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31958507,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T00:39:45.007Z","status":"online","status_checked_at":"2026-04-18T02:00:07.018Z","response_time":103,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-rest","esp32","openapi","raspberrypi"],"created_at":"2024-12-02T09:17:24.755Z","updated_at":"2026-04-18T06:08:15.119Z","avatar_url":"https://github.com/bts-lasalle-avignon-ressources.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Les API HTTP REST\n\n- [Les API HTTP REST](#les-api-http-rest)\n  - [HTTP](#http)\n    - [Présentation](#présentation)\n    - [Notion de méthode](#notion-de-méthode)\n    - [Manipulations](#manipulations)\n  - [URI et URL](#uri-et-url)\n  - [API Web](#api-web)\n  - [Exemples](#exemples)\n    - [Astronomy Picture of the Day](#astronomy-picture-of-the-day)\n    - [Star Wars](#star-wars)\n    - [IoT](#iot)\n  - [REST](#rest)\n  - [OpenAPI](#openapi)\n    - [Exemple pour un IoT ESP32](#exemple-pour-un-iot-esp32)\n    - [SwaggerHub](#swaggerhub)\n  - [Générateur](#générateur)\n    - [Swagger Editor](#swagger-editor)\n    - [Postman](#postman)\n    - [OpenAPI Generator](#openapi-generator)\n    - [SwaggerHub](#swaggerhub-1)\n    - [Swagger Codegen](#swagger-codegen)\n  - [Application serveur HTTP](#application-serveur-http)\n    - [ESP32 (C++)](#esp32-c)\n      - [Serveur web](#serveur-web)\n      - [Tests CLI avec `curl`](#tests-cli-avec-curl)\n      - [Tests avec Postman](#tests-avec-postman)\n    - [Raspberry Pi (Python)](#raspberry-pi-python)\n      - [Serveur Web](#serveur-web-1)\n      - [GPIO](#gpio)\n    - [Node.js](#nodejs)\n  - [Application cliente HTTP](#application-cliente-http)\n    - [Android Java](#android-java)\n    - [Qt C++](#qt-c)\n    - [Python](#python)\n  - [Auteurs](#auteurs)\n\n---\n\n## HTTP\n\n### Présentation\n\n[HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) (_Hypertext Transfer Protocol_) est un **protocole de communication [client-serveur](https://fr.wikipedia.org/wiki/Client-serveur)** développé pour le [World Wide Web](https://fr.wikipedia.org/wiki/World_Wide_Web) (www).\n\nC'est un protocole de la **couche Application** dans le [modèle OSI](https://fr.wikipedia.org/wiki/Mod%C3%A8le_OSI) à 7 couches et dans le [modèle DoD](https://fr.wikipedia.org/wiki/Suite_des_protocoles_Internet) à 4 couches. On utilise généralement le protocole [TCP](https://fr.wikipedia.org/wiki/Transmission_Control_Protocol) comme couche de [Transport](https://fr.wikipedia.org/wiki/Couche_transport). Un serveur HTTP utilise par défaut le port **TCP 80**.\n\nHTTP est un protocole **orienté caractères**. Les délimiteurs sont l'**espace** (` `) et le **saut de ligne** (`\\r\\n`).\n\n![](./images/client-serveur-http.png)\n\nComme tous les protocoles, HTTP est décomposé en deux parties : l'**en-tête** (_header_ ou PCI pour _Protocol Control Information_) et les **données**, qui peuvent être vides, (_payload_ ou PDU pour _Protocol Data Unit_)). Les deux parties sont délimitées par une ligne vide (`\\r\\n`) qui marquent donc la fin de l'en-tête.\n\n### Notion de méthode\n\nDans le protocole HTTP, une **méthode** est une **commande** spécifiant un **type de requête**, c'est-à-dire qu'elle demande au serveur d'effectuer une action. En général l'action concerne une ressource identifiée par l'[URL](https://fr.wikipedia.org/wiki/Uniform_Resource_Locator) qui suit le nom de la méthode.\n\nIl existe de nombreuses méthodes (`GET`, `HEAD`, `POST`, `PUT`, `DELETE`, ...). Les méthodes GET et POST sont les plus utilisées :\n\n- `GET` : C'est la méthode la plus courante pour demander une ressource. Elle ne contient pas de données mais on peut en passer sous forme de paramètres dans l'URL\n\nExemples de requêtes `GET` :\n\n```\nGET /page.html\nGET /index.html?page=42\n```\n\n- `POST` : Cette méthode est utilisée pour transmettre des données en vue d'un traitement à une ressource (elle est par exemple utilisée depuis un **formulaire HTML**).\n\n### Manipulations\n\n![](./images/manipulation-client-web.png)\n\n![](./images/manipulation-serveur-web.png)\n\n\u003e [!TIP]\n\u003e [netcat](https://fr.wikipedia.org/wiki/Netcat) (ou `nc`) est un utilitaire en ligne de commande permettant de réaliser des communications réseau (client/serveur) en utilisant les protocoles de la couche TRANSPORT UDP ou TCP. En raison de sa polyvalence, netcat est aussi appelé le « couteau suisse du TCP/IP ».\n\n## URI et URL\n\nUn [URI](https://fr.wikipedia.org/wiki/Uniform_Resource_Identifier) (_Uniform Resource Identifier_) est un identifiant d'une ressource sur un réseau sous la forme d'une chaîne de caractères.\n\nUne [URL](https://fr.wikipedia.org/wiki/Uniform_Resource_Locator) (_Uniform Resource Locator_, couramment appelée **adresse web**, est une chaîne de caractères uniforme qui permet d'identifier une ressource du [World Wide Web](https://fr.wikipedia.org/wiki/World_Wide_Web) (www) par son emplacement et de préciser le protocole internet pour la récupérer (par exemple `http` ou `https`). Elle peut localiser divers formats de données : document HTML, image, son ...\n\n\u003e [!NOTE]\n\u003e Les URL constituent un sous-ensemble des identifiants uniformes de ressource (Uniform Resource Identifier, URI), identifiants uniques d'accès à une ressource.\n\nLa syntaxe respecte une norme d’Internet. Un URI doit permettre d'identifier une ressource de manière permanente, même si la ressource est déplacée ou supprimée.\nUne URL est un URI qui décrit son mode d'accès. Par exemple, l'URL http://www.wikipedia.org/ est un URI qui identifie une ressource (page d'accueil Wikipédia) qui peut être obtenue via le protocole HTTP depuis un réseau hôte appelé www.wikipedia.org.\n\n\u003e La syntaxe générale d'une URI est décrite dans la [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) qui complète la [RFC 1738](https://datatracker.ietf.org/doc/html/rfc1738) spécifique aux URL.\n\n## API Web\n\nUne [API Web](https://fr.wikipedia.org/wiki/API_Web) est une interface de programmation d'application (API) pour un serveur Web ou un navigateur (client) Web.\n\nUne **API Web côté serveur** est servie au moyen d'un serveur Web basé sur HTTP. Elle se compose d'un ou plusieurs points d'accès exposés publiquement répondant avec des données, généralement exprimé en [XML](https://fr.wikipedia.org/wiki/Extensible_Markup_Language) ou [JSON](https://fr.wikipedia.org/wiki/JavaScript_Object_Notation).\n\n\u003e [!NOTE]\n\u003e Les [webhooks](https://fr.wikipedia.org/wiki/Webhook) sont des API Web côté serveur qui prennent en entrée un URI conçu pour être utilisé comme un canal nommé distant ou un type de rappel tel que le serveur agit en tant que client pour déréférencer l'URI fourni et **déclencher un événement sur un autre serveur qui gère cet événement**.\n\nLes points d'accès spécifient où se trouvent les ressources accessibles par les clients. Généralement l'accès se fait via une URI sur laquelle sont postées les requêtes HTTP, et dont la réponse est donc attendue. Les API Web peuvent être publiques ou privées, dans ce cas elles nécessitent un [jeton d'accès](https://fr.wikipedia.org/wiki/Jeton_d%27acc%C3%A8s) (_token_).\n\nLes API Web Web 2.0 utilisent [REST](#rest) et [SOAP](https://fr.wikipedia.org/wiki/SOAP). Les API Web _RESTful_ utilisent des méthodes HTTP pour accéder aux ressources via des paramètres encodés en URL et utilisent JSON ou XML pour transmettre des données. En revanche, les protocoles SOAP sont normalisés par le W3C et imposent l'utilisation de XML.\n\n\u003e [!IMPORTANT]\n\u003e Les API Web sont devenues omniprésentes. Il existe peu d'applications/services logiciels majeurs qui n'offrent pas une certaine forme d'API Web. Liens : https://publicapis.io/, https://rapidapi.com/hub, https://developers.google.com/apis-explorer?hl=fr et https://nordicapis.com/13-api-directories-to-help-you-discover-apis/\n\n## Exemples\n\n### Astronomy Picture of the Day\n\nUn exemple d'API Web populaire est l'API Astronomy Picture of the Day exploitée par l'agence spatiale américaine NASA. Il s'agit d'une API côté serveur utilisée pour récupérer des photographies de l'espace ou d'autres images d'intérêt pour les astronomes, ainsi que des métadonnées sur les images.\n\nL'API Web a un point de terminaison : `https://api.nasa.gov/planetary/apod`\n\nCe point de terminaison accepte les requêtes GET : `https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY\u0026date=1996-12-03`\n\nLes paramètres de cette API sont écrits dans un format connu sous le nom de **chaîne de requête**, qui est séparé par un point d'interrogation (`?`) du point de terminaison. Une esperluette (`\u0026`) sépare les paramètres de la chaîne de requête les uns des autres. Ensemble, le point de terminaison et la chaîne de requête forment une URL qui détermine la manière dont l'API répondra. Cette URL est également connue sous le nom de **requête** ou d'**appel d'API**.\n\nCette requête GET affiche à l'utilisateur un résultat (ici en [JSON](https://fr.wikipedia.org/wiki/JavaScript_Object_Notation)) appelé **valeur de retour**.\n\n```json\n{\n \"date\":\"1996-12-03\",\n \"explanation\":\"Like a butterfly,\\r a white dwarf star begins its life\\r by casting off a cocoon that enclosed its former self. In this\\r analogy, however, the Sun would be\\r a caterpillar\\r and the ejected shell of gas would become the prettiest of all!\\r The above cocoon, the planetary nebula\\r designated NGC 2440, contains one of the hottest white dwarf stars known.\\r The white dwarf can be seen as the bright dot near the photo's\\r center. Our Sun will eventually become a \\\"white dwarf butterfly\\\",\\r but not for another 5 billion years. The above false color image recently entered the public domain\\r and was post-processed by F. Hamilton.\\r\",\n \"hdurl\":\"https://apod.nasa.gov/apod/image/9612/ngc2440_hst2_big.jpg\",\n \"media_type\":\"image\",\n \"service_version\":\"v1\",\n \"title\":\"Cocoon of a New White Dwarf\\r\\nCredit:\",\n \"url\":\"https://apod.nasa.gov/apod/image/9612/ngc2440_hst2.jpg\"\n}\n```\n\n### Star Wars\n\nBienvenue sur [swapi](https://swapi.dev/), l'API Star Wars ! [swapi.dev](https://swapi.dev/) est une API complètement ouverte sans aucune authentification pour interroger et obtenir des données.\n\nL'URL racine de l'API est : `https://swapi.dev/api/`\n\nDocumentation : https://swapi.dev/documentation\n\n```bash\n$ curl -k --location https://swapi.dev/api/people/1/\n{\"name\":\"Luke Skywalker\",\"height\":\"172\",\"mass\":\"77\",\"hair_color\":\"blond\",\"skin_color\":\"fair\",\"eye_color\":\"blue\",\"birth_year\":\"19BBY\",\"gender\":\"male\",\"homeworld\":\"https://swapi.dev/api/planets/1/\",\"films\":[\"https://swapi.dev/api/films/1/\",\"https://swapi.dev/api/films/2/\",\"https://swapi.dev/api/films/3/\",\"https://swapi.dev/api/films/6/\"],\"species\":[],\"vehicles\":[\"https://swapi.dev/api/vehicles/14/\",\"https://swapi.dev/api/vehicles/30/\"],\"starships\":[\"https://swapi.dev/api/starships/12/\",\"https://swapi.dev/api/starships/22/\"],\"created\":\"2014-12-09T13:50:51.644000Z\",\"edited\":\"2014-12-20T21:17:56.891000Z\",\"url\":\"https://swapi.dev/api/people/1/\"}\n```\n\n### IoT\n\n- Gestion d'un éclairage connecté Philips Hue : [API Philips Hue REST](https://github.com/bts-lasalle-avignon-ressources/PhilipsHue) (HTTPS)\n- Gestion d'une prise électrique : [API REST myStrom](https://github.com/bts-lasalle-avignon-ressources/myStrom) (HTTP)\n\n## REST\n\n[REST](https://fr.wikipedia.org/wiki/Representational_state_transfer) (_REpresentational State Transfer_) est un style d'architecture logicielle définissant un ensemble de contraintes à utiliser pour créer des [services web](https://fr.wikipedia.org/wiki/Service_web).\n\nLes services web conformes au style d'architecture REST sont nommés **services web RESTful**.\n\nLes services web REST permettent aux systèmes effectuant des requêtes de manipuler des ressources web via leurs représentations textuelles à travers un ensemble d'opérations uniformes et prédéfinies sans état.\n\nDans un service web REST, les requêtes effectuées sur l'URI d'une ressource produisent une réponse dont le corps est formaté en [HTML](https://fr.wikipedia.org/wiki/Hypertext_Markup_Language), [XML](https://fr.wikipedia.org/wiki/Extensible_Markup_Language), [JSON](https://fr.wikipedia.org/wiki/JavaScript_Object_Notation) ou un autre format.\n\nLorsque le protocole [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) est utilisé, comme c'est souvent le cas, les méthodes HTTP généralement utilisées sont `GET` , `PUT`, `DELETE` et `POST`.\n\nLa communication client-serveur s'effectue sans conservation de l'état de la session de communication sur le serveur entre deux requêtes successives. Les requêtes du client contiennent donc toute l'information nécessaire pour que le serveur puisse y répondre.\n\nLes API REST basées sur HTTP sont définies par8 :\n\n- un **URI** de base, comme `http://api.example.com/collection/` ;\n- des **méthodes HTTP** standards (par exemple : `GET`, `POST`, `PUT`, `PATCH` et `DELETE`) ;\n- un **type de médias** pour les **données** permettant une transition d'état (par exemple : `application/json` ou `application/vnd.collection+json` pour [API JSON](https://jsonapi.org/), etc.).\n\nLe tableau suivant indique comment les méthodes HTTP sont généralement utilisées dans une API REST :\n\n|URI|GET|POST|PUT|PATCH|DELETE|\n|---|---|---|---|---|---|\n|`http://api.exemple.com/collection/`|Récupère les URI des ressources membres de la ressource `collection` dans le corps de la réponse.|Crée une ressource membre dans la ressource `collection` en utilisant les instructions du corps de la requête.|Remplace toutes les représentations des ressources membres de la ressource `collection` par la représentation dans le corps de la requête ou crée la ressource `collection` si elle n'existe pas.|Met à jour toutes les représentations des ressources membres de la ressource `collection` en utilisant les instructions du corps de la requête|Supprime toutes les représentations des ressources membres de la ressource `collection`.|\n|`http://api.exemple.com/collection/item3`|Récupère une représentation de la ressource membre dans le corps de la réponse.|Crée une ressource membre dans la ressource membre en utilisant les instructions du corps de la requête.|Remplace toutes les représentations de la ressource membre ou crée la ressource membre si elle n'existe pas par la représentation dans le corps de la requête.|Met à jour toutes les représentations de la ressource membre ou crée éventuellement la ressource membre|Supprime toutes les représentations de la ressource membre.|\n\n\u003e [!CAUTION]\n\u003e Il n'y a pas de norme officielle pour les API REST, parce que REST est une architecture et non un protocole.\n\n## OpenAPI\n\n[OpenAPI](https://swagger.io/specification/) est une norme de description des API HTTP conformes à l’architecture REST. \n\n\u003e La spécification OpenAPI v3 actuelle découle d’un projet antérieur nommé [Swagger](https://swagger.io/) jusqu'à la v2.\n\nSpécifications : https://swagger.io/specification/ et sa documentation : https://swagger.io/docs/specification/about/\n\nÀ partir d'une spécification d'API, il est possible :\n\n- d'obtenir une documentation : http://swagger.io/swagger-ui/, ...\n- de générer le code (client/serveur) : http://swagger.io/swagger-codegen/, ...\n\nIl est possible d'écrire la spécification de l'API en [JSON](https://fr.wikipedia.org/wiki/JavaScript_Object_Notation) dans un fichier `swagger.json` ou en [YAML](https://fr.wikipedia.org/wiki/YAML) dans un fichier `openapi.yaml`.\n\nPour cela, on peut utiliser l'[éditeur en ligne](http://editor.swagger.io/) : http://editor.swagger.io/.\n\n\u003e [Swagger UI](http://swagger.io/swagger-ui/) peut être utilisé en local sur la machine : https://swagger.io/docs/open-source-tools/swagger-editor/\n\nLa structure de base du fichier possède notamment les propriétés suivantes :\n\n- `openapi` : indique la version des spécifications utilisées\n- `info` : décrit des informations (métadonnées) sur l'API\n- `servers` : définit les paramètres, comme l'[URL](https://fr.wikipedia.org/wiki/Uniform_Resource_Locator) de base, du (ou des) serveur(s)\n- `paths` : définit les [URL](https://fr.wikipedia.org/wiki/Uniform_Resource_Locator)s et les opérations de l'API (`get`, `post`, ...)\n- `components` : contient un ensemble d’objets réutilisables et explicitement référencés à partir des propriétés définies dans `paths`\n\n### Exemple pour un IoT ESP32\n\nL'exemple de base présenté ici tourne autour d'un [ESP32](https://fr.wikipedia.org/wiki/ESP32). On souhaite définir une API REST pour gérer des Leds rouges et vertes reliées sur les broches [GPIO](https://fr.wikipedia.org/wiki/General_Purpose_Input/Output).\n\n\u003e La spécification complète : [specifications/openapi-v1.yaml](./specifications/openapi-v1.yaml)\n\nOn commence par définir les propriétés `openapi` et `info` :\n\n```yaml\nopenapi: 3.0.3\ninfo:\n  title: API Exemple ESP32\n  version: \"1.0\"\n  description: Voir [api-http-rest](https://github.com/bts-lasalle-avignon-ressources/api-http-rest)\n  contact:\n    name: OpenExempleESP32\n    email: tvaira@free.fr\n    url: http://tvaira.free.fr\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0.html\n```\n\nPuis, on définit 3 serveurs dans la propriété `servers` :\n\n```yaml\n...\nservers:\n  - url: http://{adresseIPESP32}\n    description: L'IoT ESP32\n    variables:\n      adresseIPESP32:\n        default: 192.168.0.1\n        description: |\n          Aller sur http://iot-esp32.local/\n  - url: http://localhost:5000\n    description: Test en Python et Node.js\n  - url: https://virtserver.swaggerhub.com/TVAIRA/ESP32/1.0\n    description: SwaggerHub API Auto Mocking\n```\n\n\u003e Le serveur `SwaggerHub` permettra de tester l'API en simulation (notion de [mock](https://fr.wikipedia.org/wiki/Mock_(programmation_orient%C3%A9e_objet))) avec les propriétés `example` définis dans la spécification.\n\nL'API est ensuite documentée avec les propriétés `paths` et `components` :\n\n- pour une requête `GET` sur `/leds` :\n\n```yaml\n...\npaths:\n  /leds:\n    get:\n      summary: Lister les leds\n      description: Lister toutes les leds disponibles\n      operationId: getLeds\n      tags:\n        - leds\n      responses:\n        \"200\":\n          description: Succès de l'opération\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/getLeds\"\n...\n```\n\nLa réponse à cette requête retourne un objet `getLeds` référencé dans la propriété `components` :\n\n```yaml\n...\ncomponents:\n  schemas:\n    getLeds:\n      type: array\n      items:\n        $ref: \"#/components/schemas/Led\"\n      example:\n        [\n          { \"idLed\": 1, \"etat\": false, \"couleur\": \"rouge\", \"broche\": 5 },\n          { \"idLed\": 2, \"etat\": false, \"couleur\": \"verte\", \"broche\": 16 },\n        ]\n    Led:\n      type: object\n      description: Une Led\n      required:\n        - idLed\n        - etat\n      properties:\n        idLed:\n          type: integer\n          format: int32\n        etat:\n          type: boolean\n          description: |\n            `true` si la led est allumée sinon `false`\n        couleur:\n          type: string\n          enum:\n            - rouge\n            - verte\n            - orange\n        broche:\n          type: integer\n          description: GPIO OUTPUT\n          format: int32\n          enum:\n            - 4\n            - 5\n            - 13\n            - 14\n            - 16\n            - 17\n            - 18\n            - 19\n            - 21\n            - 22\n            - 23\n            - 25\n            - 26\n            - 27\n            - 32\n            - 33\n...\n```\n\nOn obtiendra une réponse en JSON de ce type :\n\n```json\n[\n    {\n        \"idLed\": 1,\n        \"etat\": false,\n        \"couleur\": \"rouge\",\n        \"broche\": 4\n    },\n    {\n        \"idLed\": 2,\n        \"etat\": false,\n        \"couleur\": \"verte\",\n        \"broche\": 16\n    }\n]\n```\n\n\u003e La spécification complète : [specifications/openapi-v1.yaml](./specifications/openapi-v1.yaml)\n\nEn résumé :\n\n| Requête HTTP            | Description                        |\n|:-----------------------:|:----------------------------------:|\n| **GET** /leds           | Lister les leds                    |\n| **POST** /led           | Ajouter une Led                    |\n| **DELETE** /led/{idLed} | Supprimer une Led                  |\n| **GET** /led/{idLed}    | Obtenir les détails d\u0026#x27;une Led |\n| **PUT** /led/{idLed}    | Mettre à jour une Led              |\n| **POST** /led/{idLed}   | Mettre à jour une Led              |\n\nAvec l'[éditeur en ligne](http://editor.swagger.io/) : http://editor.swagger.io/.\n\n![](./images/openapi-operations.png)\n\n![](./images/openapi-schemas.png)\n\n### SwaggerHub\n\n[SwaggerHub](https://swagger.io/tools/swaggerhub/) est une plateforme pour créer, concevoir, documenter et tester des API (privées et publiques). Il propose un éditeur interactif, un portail de documentation hébergé, et [SwaggerHub Explore](https://explore.swaggerhub.com/) qui permet d'interagir avec les API.\n\n![](./images/swaggerhub-tvaira.png)\n\nSwaggerHub Explore permet de tester l'API :\n\n![](./images/swaggerhub-explore-tvaira.png)\n\n\u003e Le serveur [virtserver.swaggerhub.com](https://virtserver.swaggerhub.com/TVAIRA/ESP32/1.0) permet de tester l'API en simulation (notion de [mock](https://fr.wikipedia.org/wiki/Mock_(programmation_orient%C3%A9e_objet))) avec les `example` définis dans la spécification.\n\nTests :\n\n![](./images/virtserver-swaggerhub-tvaira.png)\n\n```bash\n$ curl -k --location https://virtserver.swaggerhub.com/TVAIRA/ESP32/1.0/leds\n[ {\n  \"idLed\" : 1,\n  \"etat\" : false,\n  \"couleur\" : \"rouge\",\n  \"broche\" : 4\n}, {\n  \"idLed\" : 2,\n  \"etat\" : false,\n  \"couleur\" : \"verte\",\n  \"broche\" : 5\n} ]\n\n$ curl -k --location https://virtserver.swaggerhub.com/TVAIRA/ESP32/1.0/led/1\n{\n  \"idLed\" : 1,\n  \"etat\" : false,\n  \"couleur\" : \"rouge\",\n  \"broche\" : 4\n}\n```\n\n## Générateur\n\nIl existe de nombreux outils qui permettent la génération automatique (dans de très nombreux langages et _frameworks_) de bibliothèques clientes d'API HTTP, de _stubs_ de serveur et de documentation.\n\n### Swagger Editor\n\nIl est possible de générer le code du client directement dans [Swagger Editor](http://swagger.io/swagger-ui/) :\n\n![](./images/generate-server.png)\n\n![](./images/generate-client.png)\n\n### Postman\n\nDes utilitaires comme [Postman](https://www.postman.com/) fournissent des extraits de code à réutiliser :\n\n![](./images/postman-code-snippet.png)\n\nPar exemple pour Java :\n\n![](./images/postman-code-snippet-java.png)\n\n### OpenAPI Generator\n\nIl existe aussi [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator-cli) qui permet la génération de bibliothèques clientes d'API HTTP avec une spécification [OpenAPI](https://swagger.io/specification/).\n\nLiens :\n\n- https://github.com/OpenAPITools/openapi-generator-cli\n- https://openapi-generator.tech/docs/installation/\n\nInstallation :\n\n```bash\n$ npm install -g @openapitools/openapi-generator-cli\n\nnpx openapi-generator-cli version\nDid set selected version to 7.1.0\n7.1.0\n```\n\nGénération de code Python :\n\n```bash\n$ npx @openapitools/openapi-generator-cli generate -g python -i https://api.redocly.com/registry/bundle/openhue/openhue/v2/openapi.yaml -o my-openhue-project\n```\n\n### SwaggerHub\n\nLa plateforme [SwaggerHub](https://swagger.io/tools/swaggerhub/) permet un accès au générateur Codegen qui permet de générer du code automatiquement :\n\n![](./images/codegen-client.png)\n\n![](./images/codegen-serveur.png)\n\n### Swagger Codegen\n\n[Swagger Codegen](https://github.com/swagger-api/swagger-codegen/) est aussi un générateur qui permet la génération automatique (dans de très nombreux langages et _frameworks_) de bibliothèques clientes d'API HTTP, de _stubs_ de serveur et de documentation.\n\nSans installation :\n\n```bash\n$ wget https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/3.0.52/swagger-codegen-cli-3.0.52.jar -O swagger-codegen-cli.jar\n\n$ java -jar swagger-codegen-cli.jar --help\n```\n\nPar exemple, pour générer un client en Python :\n\n```bash\n$ java -jar swagger-codegen-cli.jar generate -i ./specifications/openapi-v1.yaml -l python -o /var/tmp/test/\n```\n\nInstallation :\n\n```bash\n$ git clone https://github.com/swagger-api/swagger-codegen\n$ cd swagger-codegen/\n$ ./mvn clean package\n```\n\nPar exemple, pour générer un client en Python :\n\n```bash\n$ java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate -i ./specifications/openapi-v1.yaml -l python -o /var/tmp/test/\n```\n\n\u003e Des scripts sont fournis dans [bin/](https://github.com/swagger-api/swagger-codegen/tree/master/bin) pour de très nombreux langages et platefromes.\n\n## Application serveur HTTP\n\n### ESP32 (C++)\n\nL'exemple de base présenté ici tourne autour d'un [ESP32](https://fr.wikipedia.org/wiki/ESP32). L'API REST définie ci-dessus (cf. [specifications/openapi-v1.yaml](./specifications/openapi-v1.yaml)) va permettre de gérer des Leds rouges et vertes reliées sur les broches [GPIO](https://fr.wikipedia.org/wiki/General_Purpose_Input/Output).\n\n#### Serveur web\n\n\u003e Code source complet : [src/serveur-esp32/](src/serveur-esp32/)\n\nOn réalise le serveur web en héritant de la classe [WebServer](https://github.com/espressif/arduino-esp32/blob/master/libraries/WebServer/src/WebServer.h)  :\n\n```cpp\n#include \u003cWebServer.h\u003e\n\n#define PORT_SERVEUR_WEB 80\n\nclass ServeurWeb : public WebServer\n{\nprivate:\n    // ...\n    void installerGestionnairesRequetes();\n    void afficherAccueil();\n    void traiterRequeteGetLeds();\n    void traiterRequeteNonTrouvee();\n    // ...\n\npublic:\n    ServeurWeb(uint16_t port = PORT_SERVEUR_WEB);\n    // ...\n    void traiterRequetes();\n    // ...\n};\n```\n\nExtrait de la classe [WebServer](https://github.com/espressif/arduino-esp32/blob/master/libraries/WebServer/src/WebServer.h) :\n\n```cpp\nclass WebServer\n{\npublic:\n    //...\n    typedef std::function\u003cvoid(void)\u003e THandlerFunction;\n\n    void on(const Uri \u0026uri, THandlerFunction fn);\n    void on(const Uri \u0026uri, HTTPMethod method, THandlerFunction fn);\n    void on(const Uri \u0026uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads\n    void onNotFound(THandlerFunction fn);  //called when handler is not assigned\n    //...\n};\n```\n\n\u003e [!IMPORTANT]\n\u003e Les explications sur l'utilisation de `std::function` sont fournies dans ce [document](https://github.com/bts-lasalle-avignon-ressources/callback/).\n\n**Les méthodes `on()` (et `onNotFound()`) vont permettre de définir les opérations de l'API REST.**\n\nElles attendent en paramètre la **fonction de rappel** qui sera déclenchée lors d'une requête HTTP et qui en assurera le traitement.\n\nMais les fonctions de rappel sont déclarées dans la classe `ServeurWeb` (`traiterRequeteGetLeds()` par exemple) et cela pose un problème en C++ : les fonctions déclarées au sein d'une classe sont nommées des fonctions membres ou **méthodes**. Et ces méthodes n’existeront **seulement** lors de l'instanciation d'un objet de cette classe.\n\nLa solution est d'utiliser l'appel `bind()` pour obtenir l'**adresse de la méthode d'un objet**. La fonction `bind()` reçoit en paramètre la méthode (`\u0026ServeurWeb::traiterRequeteGetLeds` par exemple) et l'adresse de l'objet (ici `this`) qui la possède et retourne l'adresse de cette méthode.\n\n\u003e [!IMPORTANT]\n\u003e Les explications sur l'utilisation de `std::bind` et des fonctions de rappel (_callback_)sont fournies dans ce [document](https://github.com/bts-lasalle-avignon-ressources/callback/).\n\nL'appel `bind()` permet d'utiliser les méthodes d'une classe comme fonction de rappel :\n\n```cpp\nServeurWeb::ServeurWeb(uint16_t port /*= PORT_SERVEUR_WEB*/) :  WebServer(PORT_SERVEUR_WEB)\n{\n}\n\nvoid ServeurWeb::installerGestionnairesRequetes()\n{\n    // Installe les gestionnaires de requêtes\n    on(\"/\", HTTP_GET, std::bind(\u0026ServeurWeb::afficherAccueil, this));\n    on(\"/leds\", HTTP_GET, std::bind(\u0026ServeurWeb::traiterRequeteGetLeds, this));\n    // ...\n    onNotFound(std::bind(\u0026ServeurWeb::traiterRequeteNonTrouvee, this));\n\n    // Démarre le serveur\n    begin();\n}\n\nvoid ServeurWeb::traiterRequetes()\n{\n    handleClient();\n}\n\nvoid ServeurWeb::afficherAccueil()\n{\n    String message = \"\u003ch1\u003eBienvenue ...\u003c/h1\u003e\\n\";\n    // ...\n    message += \"\u003cp\u003eLaSalle Avignon v1.0\u003c/p\u003e\\n\";\n    send(200, F(\"text/html\"), message);\n}\n\nvoid ServeurWeb::traiterRequeteGetLeds()\n{\n  // ...\n}\n\nvoid ServeurWeb::traiterRequeteNonTrouvee()\n{\n    String message = \"404 File Not Found\\r\\n\";\n    send(404, \"text/plain\", message);\n}\n```\n\nL'API REST est définie dans la méthode :\n\n```cpp\nvoid ServeurWeb::installerGestionnairesRequetes()\n{\n    on(\"/\", HTTP_GET, std::bind(\u0026ServeurWeb::afficherAccueil, this));\n    on(\"/leds\", HTTP_GET, std::bind(\u0026ServeurWeb::traiterRequeteGetLeds, this));\n    on(UriRegex(\"/led/([1-\" + String(NB_LEDS_MAX) + \"]+)$\"),\n       HTTP_GET,\n       std::bind(\u0026ServeurWeb::traiterRequeteGetLed, this));\n    on(UriRegex(\"/led/([1-\" + String(NB_LEDS_MAX) + \"]+)$\"),\n       HTTP_POST,\n       std::bind(\u0026ServeurWeb::traiterRequeteUpdateLedWithForm, this));\n    on(UriRegex(\"/led/([1-\" + String(NB_LEDS_MAX) + \"]+)$\"),\n       HTTP_PUT,\n       std::bind(\u0026ServeurWeb::traiterRequeteUpdateLed, this));\n    on(UriRegex(\"/led/([1-\" + String(NB_LEDS_MAX) + \"]+)$\"),\n       HTTP_DELETE,\n       std::bind(\u0026ServeurWeb::traiterRequeteDeleteLed, this));\n    on(\"/led\", HTTP_POST, std::bind(\u0026ServeurWeb::traiterRequeteAddLed, this));\n    onNotFound(std::bind(\u0026ServeurWeb::traiterRequeteNonTrouvee, this));\n}\n```\n\nLa méthode `traiterRequeteGetLeds()` retourne une réponse en JSON de ce type :\n\n```json\n[\n    {\n        \"idLed\": 1,\n        \"etat\": false,\n        \"couleur\": \"rouge\",\n        \"broche\": 4\n    },\n    {\n        \"idLed\": 2,\n        \"etat\": false,\n        \"couleur\": \"verte\",\n        \"broche\": 16\n    }\n]\n```\n\nPour manipuler les données en JSON, on utilise la classe [ArduinoJson](https://arduinojson.org/). Il existe un [assistant](https://arduinojson.org/v6/assistant/) pour aider à manipuler ce type de données.\n\nPour les requêtes `/led/{idLed}`, on utilise une [expression régulière](https://fr.wikipedia.org/wiki/Expression_r%C3%A9guli%C3%A8re) sur l'URI avec la classe `UriRegex`. L'[expression régulière](https://fr.wikipedia.org/wiki/Expression_r%C3%A9guli%C3%A8re) `\"/led/([1-8]+)$\"` (on a défini `NB_LEDS_MAX` à `8`) permettra d'accepter seulement les requêtes pour des `idLed` compris entre un `1` et `8`. Les autres requêtes seront redigirées vers la méthode `traiterRequeteNonTrouvee()` qui retournera une erreur `404 Led non trouvée` comme cela a été définie dans l'API.\n\nPour retourner les réponses, on utilise la méthode `send()` de la classe [WebServer](https://github.com/espressif/arduino-esp32/blob/master/libraries/WebServer/src/WebServer.h) :\n\n- une réponse `200` :\n\n```cpp\ndocumentJSON.clear();\nJsonObject objetLed  = documentJSON.createNestedObject();\n\n// exemple :\nobjetJSON[\"idLed\"]   = 1;\nobjetJSON[\"etat\"]    = false;\nobjetJSON[\"couleur\"] = \"rouge\";\nobjetJSON[\"broche\"]  = 4;\n\nchar buffer[TAILLE_JSON];\nserializeJson(documentJSON, buffer);\nsend(200, \"application/json\", buffer);\n```\n\n- une réponse `400` :\n\n```cpp\nsend(400, \"application/json\", \n          \"{\\\"code\\\": 2,\\\"message\\\": \\\"La demande est invalide\\\"}\");\n```\n\nLa classe [WebServer](https://github.com/espressif/arduino-esp32/blob/master/libraries/WebServer/src/WebServer.h) fournit des méthodes pour traiter les requêtes reçues :\n\n- `uri()` : retourne l'URL de la requête\n- `method()` : retourne la commande spécifiant le type de requête (`HTTP_GET`, `HTTP_POST`, ...)\n- `arg()` : retourne un paramètre de la requête\n\nPar exemple, si la requête possède des données JSON dans `Body`, on pourra y accéder de la manière suivante :\n\n```cpp\nSerial.print(F(\"Body : \"));\nSerial.println(arg(\"plain\"));\n\nString body = arg(\"plain\");\nDeserializationError erreur = deserializeJson(documentJSON, body);\nJsonObject objetJSON = documentJSON.as\u003cJsonObject\u003e();\nif(objetJSON.containsKey(\"idLed\"))\n{\n    Serial.print(F(\"idLed : \"));\n    Serial.println(documentJSON[\"idLed\"].as\u003cint\u003e());\n}\n```\n\n#### Tests CLI avec `curl`\n\nIl est évidemment possible d'interagir avec une API Web tout simplement avec la commande `curl` (ou `wget`).\n\n- Lister les Leds (`GET`) :\n\n```bash\n$ curl --location http://192.168.52.196/leds\n[{\"idLed\":1,\"etat\":false,\"couleur\":\"rouge\",\"broche\":4},{\"idLed\":2,\"etat\":false,\"couleur\":\"verte\",\"broche\":5}]\n```\n\n- Modifier une Led (`PUT`) :\n\n```bash\n$ curl --location --request PUT 'http://192.168.52.196/led/2' \\\n--header 'Content-Type: application/x-www-form-urlencoded' \\\n--header 'Accept: application/json' \\\n--data-urlencode 'idLed=2' \\\n--data-urlencode 'etat=true' \\\n--data-urlencode 'couleur=verte' \\\n--data-urlencode 'broche=16'\n[{\"idLed\":2,\"etat\":true,\"couleur\":\"verte\",\"broche\":16}]\n```\n\n- Modifier une Led (`POST`) :\n\n```bash\n$ curl --location 'http://192.168.52.196/led/1' \\\n--header 'Content-Type: application/json' \\\n--data '{\n  \"idLed\": \"1\",\n  \"etat\": true,\n  \"couleur\": \"rouge\",\n  \"broche\": 5\n}'\n[{\"idLed\":1,\"etat\":true,\"couleur\":\"rouge\",\"broche\":5}]\n```\n\n- Obtenir les informations sur une Led (`GET`) :\n\n```bash\n$ curl --location http://192.168.52.196/led/1\n[{\"idLed\":1,\"etat\":true,\"couleur\":\"rouge\",\"broche\":5}]\n$ curl --location http://192.168.52.196/led/2\n[{\"idLed\":2,\"etat\":true,\"couleur\":\"verte\",\"broche\":16}]\n```\n\n- Ajouter une nouvelle Led (`POST`) :\n\n```bash\n$ curl --location 'http://192.168.52.196/led' \\\n--header 'Content-Type: application/json' \\\n--header 'Accept: application/json' \\\n--data '{\n  \"couleur\": \"orange\",\n  \"broche\": 17\n}'\n[{\"idLed\":3,\"etat\":false,\"couleur\":\"orange\",\"broche\":17}]\n```\n\n- Supprimer une Led (`DELETE`) :\n\n```bash\n$ curl --location --request DELETE 'http://192.168.52.196/led/3'\n```\n\n- Quelques erreurs :\n\n```bash\n$ curl --location http://192.168.52.196/led/4\n404 Led non trouvée\n```\n\nUne broche invalide :\n\n```bash\n$ curl --location 'http://192.168.52.196/led' \\\n--header 'Content-Type: application/json' \\\n--header 'Accept: application/json' \\\n--data '{\n  \"couleur\": \"orange\",\n  \"broche\": 40\n}'\n{\"code\": 2,\"message\": \"La demande est invalide\"}\n```\n\n#### Tests avec Postman\n\n[Postman](https://fr.wikipedia.org/wiki/Postman_(logiciel)) est une plateforme pour la construction, l'utilisation et les tests d'API Web.\n\nLien : https://www.postman.com/\n\nTélécharger et installer la version de [Postman](https://dl.pstmn.io/download/latest/linux_64) pour Linux : https://dl.pstmn.io/download/latest/linux_64\n\nOu à partir du gestionnaire de paquets _snap_ :\n\n```bash\n$ sudo snap install postman\n```\n\n![](./images/demarrer-postman-ubuntu.png)\n\n\u003e Créer un compte si besoin.\n\nIl existe aussi un outil en ligne de commande Postman CLI :\n\n```bash\n$ curl -o- \"https://dl-cli.pstmn.io/install/linux64.sh\" | sh\n```\n\nEt il existe une extension pour Visual Studio Code : https://marketplace.visualstudio.com/items?itemName=Postman.postman-for-vscode\n\n\u003e Voir aussi : [bruno](https://www.usebruno.com/), https://hevodata.com/learn/rest-clients/, ...\n\n1. On commence par importer le fichier de spécifications [openapi-v1.yaml](./specifications/openapi-v1.yaml)\n\n![](./images/postman-import-esp32.png)\n\n2. On crée un environnement et une variable `{adresseIPESP32}` dans celui-ci :\n\n![](./images/postman-environement-esp32.png)\n\n3. On teste une requête :\n\n![](./images/postman-getleds-esp32.png)\n\n### Raspberry Pi (Python)\n\nL'exemple de base présenté ici tourne autour d'un [Raspberry Pi](https://fr.wikipedia.org/wiki/Raspberry_Pi). L'API REST définie ci-dessus (cf. [specifications/openapi-v1.yaml](./specifications/openapi-v1.yaml)) va permettre de gérer des Leds rouges et vertes reliées sur les broches [GPIO](https://fr.wikipedia.org/wiki/General_Purpose_Input/Output).\n\n#### Serveur Web\n\nIl est possible de créer un serveur HTTP API REST avec [Flask](https://pypi.org/project/Flask/) en [Python](https://www.python.org/).\n\n\u003e Voir aussi [FastAPI](https://fastapi.tiangolo.com/)\n\nLiens :\n\n- [Installation](https://flask.palletsprojects.com/en/3.0.x/installation/#python-version)\n- [Quickstart](https://flask.palletsprojects.com/en/3.0.x/quickstart/)\n\n\nSi besoin :\n\n```bash\n$ sudo apt-get -y install python3-pip\n$ sudo pip3 install flask\n```\n\nExemple basique :\n\n```python\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.route(\"/\")\ndef hello_world():\n    return \"\u003cp\u003eHello, World!\u003c/p\u003e\"\n```\n\nTest :\n\n```bash\n$ flask --app app run\n * Serving Flask app 'app'\n * Debug mode: off\nWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.\n * Running on http://127.0.0.1:5000\nPress CTRL+C to quit\n```\n\nOn commence par définir l'API REST :\n\n```python\n#!/usr/bin/env python\n# encoding: utf-8\nfrom flask import Flask, request, abort, jsonify\n\n...\n\n@app.route('/leds', methods=['GET'])\ndef getLeds():\n    ...\n\n@app.route('/led/\u003cint(min=1,max=8):idLed\u003e', methods=['GET'])\ndef getLed(idLed):\n    ...\n\n@app.route('/led/\u003cint(min=1,max=8):idLed\u003e', methods=['PUT'])\ndef upadateLed(idLed):\n    ...\n\n@app.route('/led/\u003cint(min=1,max=8):idLed\u003e', methods=['POST'])\ndef updateLedWithForm(idLed):\n    ...\n\n@app.route('/led/\u003cint(min=1,max=8):idLed\u003e', methods=['DELETE'])\ndef deleteLed(idLed):\n    ...\n\n@app.route('/led', methods=['POST'])\ndef addLed():\n    ...\n\n@app.errorhandler(400)\ndef traiterRequeteNonValide(error):\n    ...\n\n@app.errorhandler(404)\ndef traiterRequeteNonTrouvee(error):\n    ...\n```\n\nPour les besoins de l'exemple, on définit une classe `Led` :\n\n```python\nclass Led(object):\n    def __init__(self, idLed: int=None, etat: bool=None, couleur: str=None, broche: int=None):\n        self._idLed = idLed\n        self._etat = etat\n        self._couleur = couleur\n        self._broche = broche\n...\n```\n\nEt on crée deux `Led` :\n\n```python\n#!/usr/bin/env python\n# encoding: utf-8\nfrom flask import Flask, request, abort, jsonify\nimport led\n\nNB_LEDS_MAX = 8\nleds = {}\nleds[1] = led.Led(1, False, \"rouge\", 5)\nleds[2] = led.Led(2, False, \"verte\", 16)\n\napp = Flask(__name__)\n\n...\n```\n\nPar exemple l'implémentation du traitement de la requête `GET` sur `http://127.0.0.1:5000/leds` pour lister les Leds :\n\n```python\n@app.route('/leds', methods=['GET'])\ndef getLeds():\n    ledsValides = (led for led in leds.values() if led != None)\n    return jsonify([led.serialize() for led in ledsValides])\n```\n\ndonnera :\n\n```bash\n$ curl --location http://127.0.0.1:5000/leds\n[{\"broche\":5,\"couleur\":\"rouge\",\"etat\":false,\"idLed\":1},{\"broche\":16,\"couleur\":\"verte\",\"etat\":false,\"idLed\":2}]\n```\n\nEt la requête `GET` sur `http://127.0.0.1:5000/led/idLed` pour obtenir les informations sur une Led :\n\n```python\n@app.route('/led/\u003cint(min=1,max=8):idLed\u003e', methods=['GET'])\ndef getLed(idLed):\n    if idLed in leds:\n        if leds[idLed] == None:\n            abort(404, description=\"Led non trouvée\")\n        return jsonify(leds[idLed].serialize())\n    else:\n        abort(404, description=\"Led non trouvée\")\n```\n\ndonnera :\n\n```bash\n$ curl --location http://127.0.0.1:5000/led/1\n{\"broche\":5,\"couleur\":\"rouge\",\"etat\":false,\"idLed\":1}\n\n$ curl --location http://127.0.0.1:5000/led/5\nLed non trouvée\n```\n\n\u003e Code source complet : [src/serveur-python-flask/](src/serveur-python-flask/)\n\nTest :\n\n```bash\n$ cd src/serveur-python-flask\n$ pip3 install -r requirements.txt\n$ flask --app app run\n * Serving Flask app 'app'\n * Debug mode: off\nWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.\n * Running on http://127.0.0.1:5000\nPress CTRL+C to quit\n```\n\n\u003e [Deploying to Production](https://flask.palletsprojects.com/en/3.0.x/deploying/)\n\n#### GPIO\n\n[GPIO](https://fr.wikipedia.org/wiki/General_Purpose_Input/Output) (_General Purpose Input/Output_) est un port d’entrées-sorties très\nutilisés dans le monde des microcontrôleurs et de l’électronique embarquée.\n\nLes GPIO sont gérés par les pilotes du noyau du système d’exploitation. Il n’y a pas d’entrée/sortie analogique.\n\nLinux reconnaît nativement les ports GPIO, une documentation complète est même disponible (www.kernel.org/doc/Documentation/gpio/gpio.txt).\n\nAccès système :\n\n- Les ports GPIO étaient accessibles depuis leur export dans `/sys/class/gpio/` via `sysfs` (obsolète)\n- Depuis la version 4.8 du noyau Linux, les GPIO sont accesibles par le pilote de périphérique ABI (_Application Binary Interface_) _chardev GPIO_ via `/dev/gpiochipN` ou `/sys/bus/gpio`\n\nLien : https://www.raspberrypi.com/documentation/computers/raspberry-pi.html\n\nLa liste des GPIO est accessible avec la commande `pinout`.\n\n![](https://www.raspberrypi.com/documentation/computers/images/GPIO-Pinout-Diagram-2.png)\n\n- Une sortie peut être fixée sur un niveau haut (3V3) ou bas (0V).\n- Une entrée peut être lue comme un niveau haut (3V3) ou bas (0V).\n\n\u003e [!TIP]\n\u003e Il est possible d’utiliser de résistances internes _pull-up_ ou _pull-down_. Les GPIO2 et GPIO3 ont des résistances de _pull-up_ fixes, mais pour les autres broches, elles peuvent être configurées logiciellement. _Software PWM_ (_Pulse-Width Modulation_) disponible sur toutes les broches et _Hardware PWM_ seulement sur GPIO12, GPIO13, GPIO18, GPIO19.\n\u003e I2C : SDA (GPIO2) SCL (GPIO3) et EEPROM Data (GPIO0) EEPROM Clock (GPIO1)\n\u003e Port série (UART) : TX (GPIO14) RX (GPIO15)\n\u003e SPI :\n\u003e - SPI0 : MOSI (GPIO10) MISO (GPIO9) SCLK (GPIO11) CE0 (GPIO8) CE1 (GPIO7)\n\u003e - SPI1 : MOSI (GPIO20) MISO (GPIO19) SCLK (GPIO21) CE0 (GPIO18) CE1 (GPIO17) CE2 (GPIO16)\n\nTest avec [WiringPi](https://github.com/WiringPi/WiringPi/releases/tag/2.61-1) :\n\n- Pour un système 32 bits :\n\n```bash\n$ wget https://github.com/WiringPi/WiringPi/releases/download/2.61-1/wiringpi-2.61-1-armhf.deb\n$ sudo apt install ./wiringpi-2.61-1-armhf.deb\n```\n\n- Pour un système 64 bits :\n\n```bash\n$ wget https://github.com/WiringPi/WiringPi/releases/download/2.61-1/wiringpi-2.61-1-arm64.deb\n$ sudo apt install ./wiringpi-2.61-1-arm64.deb\n```\n\nConfiguration d'une sortie :\n\n```bash\n$ gpio -g mode 17 out\n```\n\n![](./images/gpio17.png)\n\nFixe la sortie à l'état bas :\n\n```bash\n$ gpio -g write 17 0\n\n$ gpio readall\n +-----+-----+---------+------+---+---Pi 3B--+---+------+---------+-----+-----+\n | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |\n +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+\n |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |\n |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |\n |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |\n |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 0 | IN   | TxD     | 15  | 14  |\n |     |     |      0v |      |   |  9 || 10 | 1 | IN   | RxD     | 16  | 15  |\n |  17 |   0 | GPIO. 0 |  OUT | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |\n |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |\n |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |\n |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |\n |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |\n |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |\n |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |\n |     |     |      0v |      |   | 25 || 26 | 1 | IN   | CE1     | 11  | 7   |\n |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |\n |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |\n |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |\n |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |\n |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |\n |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |\n |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |\n +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+\n | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |\n +-----+-----+---------+------+---+---Pi 3B--+---+------+---------+-----+-----+\n```\n\nFixe la sortie à l'état haut :\n\n```bash\n$ gpio -g write 17 1\n\n$ gpio readall\n +-----+-----+---------+------+---+---Pi 3B--+---+------+---------+-----+-----+\n | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |\n +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+\n |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |\n |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |\n |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |\n |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 0 | IN   | TxD     | 15  | 14  |\n |     |     |      0v |      |   |  9 || 10 | 1 | IN   | RxD     | 16  | 15  |\n |  17 |   0 | GPIO. 0 |  OUT | 1 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |\n |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |\n |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |\n |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |\n |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |\n |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |\n |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |\n |     |     |      0v |      |   | 25 || 26 | 1 | IN   | CE1     | 11  | 7   |\n |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |\n |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |\n |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |\n |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |\n |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |\n |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |\n |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |\n +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+\n | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |\n +-----+-----+---------+------+---+---Pi 3B--+---+------+---------+-----+-----+\n```\n\nLien : [Gestion des ports GPIO en Python](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#gpio-in-python)\n\nPour gérer les ports GPIO, on peut utiliser [RPi.GPIO](https://pypi.org/project/RPi.GPIO/) ou [gpiozero](https://gpiozero.readthedocs.io/en/latest/).\n\nOn l'intégre à l'application :\n\n```python\n#!/usr/bin/env python\n# encoding: utf-8\n\nfrom flask import Flask, request, abort, jsonify, Response\nimport led\nimport RPi.GPIO as GPIO\n\nNB_LEDS_MAX = 8\nleds = {}\nleds[1] = led.Led(1, False, \"rouge\", 5)\nleds[2] = led.Led(2, False, \"verte\", 17)\n\nGPIO.setmode(GPIO.BCM)\nGPIO.setup(leds[1].broche, GPIO.OUT)\nGPIO.setup(leds[2].broche, GPIO.OUT)\n\napp = Flask(__name__)\n...\n```\n\nOn va ajouter une méthode pour commander la Led dans la classe `Led` :\n\n```python\n# coding: utf-8\n\nimport RPi.GPIO as GPIO\n\nclass Led(object):\n    def __init__(self, idLed: int=None, etat: bool=None, couleur: str=None, broche: int=None):\n        self._idLed = idLed\n        self._etat = etat\n        self._couleur = couleur\n        self._broche = broche\n\n    def commander(self):\n        if self._etat:\n            GPIO.output(self._broche, GPIO.HIGH)\n        else:\n            GPIO.output(self._broche, GPIO.LOW)\n\n...\n\n    @etat.setter\n    def etat(self, etat: bool):\n        if etat is None:\n            raise ValueError(\"Valeur invalide pour etat\")\n        if etat != self._etat:\n            self._etat = etat\n            self.commander()\n```\n\n\u003e C'est un changement d'état (cf. _setter_) qui provoque une commande du GPIO associé à la Led.\n\nDémarrage du serveur en [développement](https://flask.palletsprojects.com/en/3.0.x/cli/#run-the-development-server) :\n\n```bash\nflask --app app run -h 0.0.0.0 -p 5000\n/home/pi/serveur-python-flask/app.py:16: RuntimeWarning: This channel is already in use, continuing anyway.  Use GPIO.setwarnings(False) to disable warnings.\n  GPIO.setup(leds[2].broche, GPIO.OUT)\n * Serving Flask app 'app'\n * Debug mode: off\nWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.\n * Running on all addresses (0.0.0.0)\n * Running on http://127.0.0.1:5000\n * Running on http://192.168.1.24:5000\nPress CTRL+C to quit\n```\n\nTest avec la Led \"verte\" sur le GPIO 17 (broche 11) :\n\n```bash\ncurl -H 'Content-Type: application/json' -X POST -d '{\"broche\":17,\"couleur\":\"verte\",\"etat\":true,\"idLed\":2}' http://192.168.1.24:5000/led/2\ncurl -H 'Content-Type: application/json' -X POST -d '{\"broche\":17,\"couleur\":\"verte\",\"etat\":false,\"idLed\":2}' http://192.168.1.24:5000/led/2\n```\n\n![](./images/test-led-verte-rpi.png)\n\n\u003e Code source complet : [src/serveur-python-flask/](src/serveur-python-flask/)\n\n### Node.js\n\nIl est possible de créer un serveur HTTP API REST avec [Express](http://expressjs.com/) avec [Node.js](https://nodejs.org/).\n\nTutoriel : https://node-js.fr/express/rest.html\n\n## Application cliente HTTP\n\nPour faire simple, cela revient à émettre des requêtes HTTP et le plus souvent à traiter du JSON ou du XML.\n\n### Android Java\n\nPour émettre des requêtes HTTP sous Android, il y a plusieurs possibilités. Par exemple :\n\n- le client [OkHttp](https://square.github.io/okhttp/)\n- le [client HTTP](https://cloud.google.com/java/docs/reference/google-http-client/latest/com.google.api.client.http) de l'API Google\n\nExemples d'applications Android :\n\n- Gestion d'un éclairage connecté Philips Hue : [API Philips Hue REST](https://github.com/bts-lasalle-avignon-ressources/PhilipsHue) (HTTPS)\n- Gestion d'une prise électrique : [API REST myStrom](https://github.com/bts-lasalle-avignon-ressources/myStrom) (HTTP)\n\n### Qt C++\n\nPour émettre des requêtes HTTP sous Qt, il faudra utiliser la classe [QNetworkAccessManager](https://doc.qt.io/qt-6/qnetworkaccessmanager.html) du module `network`.\n\nExemples d'applications Qt :\n\n- Gestion d'un éclairage connecté Philips Hue : [API Philips Hue REST](https://github.com/bts-lasalle-avignon-ressources/PhilipsHue) (HTTPS)\n- Gestion d'une prise électrique : [API REST myStrom](https://github.com/bts-lasalle-avignon-ressources/myStrom) (HTTP)\n\n### Python\n\n- Avec `http.client` :\n\n```python\nimport http.client\n\nconn = http.client.HTTPSConnection(\"192.168.52.187\")\npayload = ''\nheaders = {\n  'Accept': 'application/json',\n  'hue-application-key': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'\n}\nconn.request(\"GET\", \"/clip/v2/resource/light/f9a9b376-6738-4bd1-81ce-021e2ee56a82\", payload, headers)\nres = conn.getresponse()\ndata = res.read()\nprint(data.decode(\"utf-8\"))\n```\n\n- Avec `requests` :\n\n```python\nimport requests\n\nurl = \"https://187/clip/v2/resource/light/f9a9b376-6738-4bd1-81ce-021e2ee56a82\"\n\npayload = {}\nheaders = {\n  'Accept': 'application/json',\n  'hue-application-key': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'\n}\n\nresponse = requests.request(\"GET\", url, headers=headers, data=payload)\n\nprint(response.text)\n```\n\n## Auteurs\n\n- [Thierry VAIRA](thierry.vaira@gmail.com) : [tvaira.free.fr](http://tvaira.free.fr/)\n\n---\n©️ 2023 BTS LaSalle Avignon\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbts-lasalle-avignon-ressources%2Fapi-http-rest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbts-lasalle-avignon-ressources%2Fapi-http-rest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbts-lasalle-avignon-ressources%2Fapi-http-rest/lists"}