API Ecowatt Sketch JML

Bonjour à tous, Bonjour J.M.L.,

Je teste ce sketch écrit par J.M.L. :

/* ============================================
  KEEP THIS INFORMATION IF YOU USE THIS CODE

  This "API RTE ecowatt" demo code for ESP 32 is placed under the MIT license
  Copyright (c) 2022 J-M-L For the Arduino Forum

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ===============================================


  Ref : https://forum.arduino.cc/t/api-rte-ecowatt/1017281/34
*/

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// *****************************************
// *****************************************
//
//     ! ATTENTION ELEMENTS SECRETS !
//
// *****************************************
// *****************************************

const char* ssid =          "xxxxxxxxxxxx";
const char* password =      "xxxxxxxxxx";
#define identificationRTE   "OWZxxxxxxxxxxxxxxxxxxxx    w=="  // ID Client et ID Secret en base 64

// *****************************************
// *****************************************


const char * idRTE = "Basic " identificationRTE; // le compilateur se charge de concaténer "Basic " + ID Client et ID Secret en base 64

// le certificat racine (format PEM) de https://digital.iservices.rte-france.com
const char* root_ca = \
                      "-----BEGIN CERTIFICATE-----\n" \
                      "MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G\n" \
                      "A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp\n" \
                      "Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4\n" \
                      "MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG\n" \
                      "A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI\n" \
                      "hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8\n" \
                      "RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT\n" \
                      "gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm\n" \
                      "KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\n" \
                      "QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ\n" \
                      "XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw\n" \
                      "DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o\n" \
                      "LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU\n" \
                      "RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp\n" \
                      "jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK\n" \
                      "6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX\n" \
                      "mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs\n" \
                      "Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\n" \
                      "WD9f\n" \
                      "-----END CERTIFICATE-----\n";

const char* oauthURI =          "https://digital.iservices.rte-france.com/token/oauth/";
const char* signalsURI =        "https://digital.iservices.rte-france.com/open_api/ecowatt/v4/signals";
const char* signalsSandboxURI = "https://digital.iservices.rte-france.com/open_api/ecowatt/v4/sandbox/signals";


// obtention d'une transcription des codes d'erreurs spécifique à l'API RTE ou message général
String errorDescription(int code, HTTPClient& http) {
  switch (code) {
    case 401: return "l'authentification a échouée";
    case 403: return "l’appelant n’est pas habilité à appeler la ressource";
    case 413: return "La taille de la réponse de la requête dépasse 7Mo";
    case 414: return "L’URI transmise par l’appelant dépasse 2048 caractères";
    case 429: return "Le nombre d’appel maximum dans un certain laps de temps est dépassé";
    case 509: return "L‘ensemble des requêtes des clients atteint la limite maximale";
    default: break;
  }
  return http.errorToString(code);
}

// effectue une requête. Nécessite une connexion WiFi active.

bool getRTEData() {
  String oauthPayload;
  String signalsPayload;
  int codeReponseHTTP;
  const char* access_token;

  bool requeteOK = true;

  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFI non disponible. Requête impossible");
    return false;
  }

  WiFiClientSecure client;
  HTTPClient http;

  client.setCACert(root_ca);
  http.begin(client, oauthURI);

  // Specify content-type header
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  http.addHeader("Authorization", idRTE);

  // Send HTTP POST request
  codeReponseHTTP = http.POST(nullptr, 0);

  if (codeReponseHTTP == HTTP_CODE_OK) {
    oauthPayload = http.getString();
    Serial.println(oauthPayload);

    StaticJsonDocument<192> doc;
    DeserializationError error = deserializeJson(doc, oauthPayload);

    if (error) {
      Serial.print("deserializeJson() failed: ");
      Serial.println(error.c_str());
      access_token = "";
      requeteOK = false;
    } else {
      access_token = doc["access_token"];
      Serial.print("access_token : "); Serial.println(access_token);
    }
  } else {
    Serial.print("erreur HTTP POST: ");
    Serial.println(errorDescription(codeReponseHTTP, http));
    requeteOK = false;
  }

  // on libère les resources
  http.end();

  if (!requeteOK) return false;

  // on a récupéré l'access_token, on peut l'utiliser pour faire notre requête GET sur l'API
  String signalsAutorization = "Bearer ";
  signalsAutorization += String(access_token);

  // si on veut tester le code sans être gêné par la limite d'un appel toutes les 15 minutes, on utilise la sandbox
  // http.begin(client, signalsSandboxURI);

  // si on veut avoir l'information réelle, on utilise la vraie API. On est limité à un appel toutes les 15 minutes
   http.begin(client, signalsURI);

  http.addHeader("Authorization", signalsAutorization.c_str());

  // On envoie la requête HTTP GET
  codeReponseHTTP = http.GET();

  if (codeReponseHTTP == HTTP_CODE_OK) {
    signalsPayload = http.getString();
    Serial.println("----------------------");
    Serial.println("       RTE JSON");
    Serial.println("----------------------\n");
    Serial.println(signalsPayload);
  } else {
    Serial.print("erreur HTTP GET: ");
    Serial.print(codeReponseHTTP);
    Serial.print(" => ");
    Serial.println(http.errorToString(codeReponseHTTP));
    requeteOK = false;
  }
  http.end();
  return requeteOK;
}


void setup() {

  Serial.begin(115200);
  delay(1000);
  Serial.println(); Serial.print ("Sketch : "); Serial.print ("API_RTE_ECOWATT_1.2.ino"); Serial.println();

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.write('.');
  }
  Serial.print("\nConnected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());

  getRTEData();
}

void loop() {}

Qui fonctionne très bien via la SandBox mais mais qui me retourne un message d'erreur lorsque je veux obtenir les valeurs du jour.

Sketch : API_RTE_ECOWATT_1.2.ino
....
Connected to WiFi network with IP Address: 192.168.1.50
{
  "access_token" : "ZZqpw2qGkEk8rJ4JBx6cwoRRtpTsAwjufqFrIFeMKNrjIgpqLMebGF",
  "token_type" : "Bearer",
  "expires_in" : 7200
}
access_token : ZZqpw2qGkEk8rJ4JBx6cwoRRtpTsAwjufqFrIFeMKNrjIgpqLMebGF
erreur HTTP GET: 403 => 

qui correspond à : "l’appelant n’est pas habilité à appeler la ressource" dans l'API.
Pourtant, j'ai bien créé un compte et je récupère bien le Token en base 64.
Si quelqu'un en sait un peu plus, je suis preneur.

une autre question sur le fait que je ne récupère pas le libellé de l'erreur : erreur HTTP GET: 403 =>
J.M.L; s'il est dispo, saura surement me renseigner sur ce point ?

Merci d'avance à tous.

Fitness04

En dehors de la sandbox on est limité à un appel toutes les 15 minutes

cf API RTE ecowatt - #34 by J-M-L

Bonsoir J.M.L.,,

Si on dépasse la limitation de 15 mn, c'est l'erreur 429 :

case 429: return "Le nombre d’appel maximum dans un certain laps de temps est dépassé";

Pour moi c'est l'erreur 403 :

case 403: return "l’appelant n’est pas habilité à appeler la ressource";

Peut être une erreur sur le site internet lui même car je suis bien inscrit sur ce site !

Et pour ma seconde question concernant le non affichage du libellé correspondant au code erreur, vous avez une explication?

Merci.

Fitness04

je ne sais pas... il me semble que j'avais testé mais qu'avec une requête toutes les 15 minutes c'était pas top pour travailler... Clairement le token est bien soumis, peut-être ont-ils modifié le système réel par rapport au système SandBox...

pour le moment je ne peux pas tester


concernant l'autre point comme il n'y a pas le texte, ça veut dire que l'on ne rentre pas dans le case. C'est bizarre

modifiez le code comme suit

String errorDescription(int code, HTTPClient& http) {
  Serial.print("On me demande le message d'erreur pour [");
  Serial.print(code);
  Serial.println("]");

  switch (code) {
    case 401: return "l'authentification a échouée";
    case 403: return "l’appelant n’est pas habilité à appeler la ressource";
    case 413: return "La taille de la réponse de la requête dépasse 7Mo";
    case 414: return "L’URI transmise par l’appelant dépasse 2048 caractères";
    case 429: return "Le nombre d’appel maximum dans un certain laps de temps est dépassé";
    case 509: return "L‘ensemble des requêtes des clients atteint la limite maximale";
    default: break;
  }
  return http.errorToString(code);
}

on verra ainsi que est le code passé exactement

Bonjour J.M.L.

J'ai fait le test avec la modification et effectivement la routine n'est pas appelée

Sketch : API_RTE_ECOWATT_1.0.ino
....
Connected to WiFi network with IP Address: 192.168.1.50
{
  "access_token" : "IAfF9VfBjQzlK6NIbNjc9AX2JiIZeFr7GnfDpYyoz65G4eW3t1Zd6B",
  "token_type" : "Bearer",
  "expires_in" : 7200
}
access_token : IAfF9VfBjQzlK6NIbNjc9AX2JiIZeFr7GnfDpYyoz65G4eW3t1Zd6B
erreur HTTP GET: 403 => 

c'est bizarre parce que clairement vous passez dans le début de ces print et que le code est bien vu comme 403

mais ils n'appelle pas Serial.println(http.errorToString(codeReponseHTTP));??

est-ce que votre arduino plante à ce moment là ?

vous êtes bien sur un ESP32 ?

Bonsoir J.M.L.,

Je suis bien sur un ESP32 Wroom.
J'ai mis un mouchard pour voir si le programme plantait après cet appel à la fonction, et bien non.

if (codeReponseHTTP == HTTP_CODE_OK) {
    signalsPayload = http.getString();
    Serial.println("----------------------");
    Serial.println("       RTE JSON");
    Serial.println("----------------------\n");
    Serial.println(signalsPayload);
  } else {
    Serial.print("erreur HTTP GET: ");
    Serial.print(codeReponseHTTP);
    Serial.print(" => ");
    Serial.println(http.errorToString(codeReponseHTTP));
    Serial.println ("Mouchard1");
    requeteOK = false;
  }

Retourne :

Sketch : API_RTE_ECOWATT_1.0.ino
....
Connected to WiFi network with IP Address: 192.168.1.50
{
  "access_token" : "g6n7kx38qJiAa853pomuTOi8bAhDY2ciPM8m61L9LQZQvNROyevz2t",
  "token_type" : "Bearer",
  "expires_in" : 7200
}
access_token : g6n7kx38qJiAa853pomuTOi8bAhDY2ciPM8m61L9LQZQvNROyevz2t
erreur HTTP GET: 403 => 
Mouchard1

Ah c’est tout bête…. mais c’est parce qu’on n’appelle pas la fonction errorDescription()…

Il faut faire

Serial.println(errorDescription(codeReponseHTTP, http));

Désolé je lis sur mon iPhone donc tout ne saute pas aux yeux…

Bonjour J.M.L.,

Effectivement ça fonctionne.

J'ai un peu du mal à comprendre le fonctionnement de la fonction : String errorDescription(int code, HTTPClient & http) et notamment le : return http.errorToString(code);

Si vous avez un peu de temps un jour peut-être pourriez vous l'expliquer en détail ca elle parrait bien pratique...
Merci encore pour votre disponibilité et votre aide.

La première fonction renvoi un message prédéfinie en fonction du paramètre code (pour les valeurs 401, 403, 413, 414, 429, 509)
Si la valeur de code n'est pas une des valeurs précédentes, alors la fonction renvoi ce que retourne errorToString
Si tu regarde le code ce cette fonction (je suppose que le code source est là), tu pourra voir qu'en faite cette fonction fait la même chose que la première fonction errorDescription, mais avec des codes erreurs propre à la classe HTTPClient

Je ne sais pas si j'ai été assez claire ?

pour compléter ce que dit @terwal c'est que l'idée pour la fonction errorDescription() c'est de retourner un code spécifique pour ceux que l'on connait et sinon le code général HTTP. donc la fonction traite les cas 401,403,413,414,429, et 509 et sinon demande à la bibliothèque la String d'erreur associée au code.

Bonsoir J-M-L, Terwal,

Merci pour l'explication. C'est clair maintenant.

pour info l'API va évoluer


Outre sa fonction d’alerte, la nouvelle version d’Ecowatt indique les heures durant lesquelles la France peut totalement couvrir ses besoins en électricité à partir d’une production d’électricité française 100 % décarbonée.

Vous pouvez depuis le 8 novembre 2023 vous abonner à la nouvelle version de l’API Ecowatt V5 pour récupérer automatiquement l’ensemble des prévisions horaires : les signaux d’alerte (valeurs 1, 2 ou 3) ainsi que les heures Ecowatt vertueuses (valeur 0).

Afin d’assurer la continuité de service, l’ancienne version de l’API Ecowatt V4 qui ne contient que les signaux d’alerte Ecowatt, reste disponible jusqu’au 30 juin 2024.

A compter de cette date, seule la version 5 de l’API Ecowatt sera disponible, aussi nous vous recommandons d’anticiper dès que possible l’évolution de vos développements et de vous abonner à la nouvelle API.


This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.