[Enedis][Linky] Réception de la consommation pour les particuliers

Bonjour,

J’ai mis en place un scénario qui permet de récupérer les informations de linky et de les mettres dans un virtuel en passant par leurs API intermédiaire.

Pré-requis :

  • Avoir autorisé l’historique des données à 1h dans votre espace client Enedis sur l’onglet historique des mesures (ou avoir renouvellé l’autorisation, nécessaire tous les 1 ans)
  • Créer un virtuel contenant les infos suivants
    device_code
    access_token
    token_type
    refresh_token
    usage_point_id
    value
    maxPower
  • Remplacez tous les [Automatismes généraux][EnedisDomoticz] dans le scénario par le nom de vos objets par celui correspondant à votre virtuel

!!! Attention, il faut exécuter l’étape 3 assez rapidement après avoir créé le partage Enedis !!!

  1. Executer le scénario d’abord avec $code=1
  2. Aller dans les logs et rentrer le code fournit dans l’url indiqué. Ca va vous demander de vous connecter à Linky et d’accepter de partager vos données de Enedis
  3. Remettre $code=0 et mettre $firstConnexion=1 et lancer le scénario de nouveau
  4. Ensuite planifier l’execution de votre scénario une fois par jour en mettant
    $code=1;
    $firstConnexion=0;
    $refreshToken=0;
    $getDailyConsumption=0;
    $getDailyMaxPower=0;
    $getLoadCurve=0;
/* 
	PDL xxx
*/

//Configuration
$debug=0; // Mettre à 1 si vous voulez interroger les sites de tests / devs

$device_code = ''; // A compléter si vous avez activé votre code avec un autre device code que le dernier généré
$access_token = ''; // A compléter si vous avez un token obtenu autrement
$refresh_token = ''; // A compléter si vous avez un refresh token obtenu autrement
$usage_point_id = ''; // A compléter si vous avez fait l'operation autrement pour indique le PDL

$startDate = date("Y-m-d", time() - 60 * 60 * 24 * 7); // YYYY-MM-DD (J-8 pour récupérer 7 jours de data)
$avantHierDate = date("Y-m-d", time() - 60 * 60 * 24 * 2); // YYYY-MM-DD (Avant-hier)
$endDate = date("Y-m-d", time() - 60 * 60 * 24); // YYYY-MM-DD (Hier)

//Etape du processus
$code=0; // mettre à 1 si vous avez besoin d'un code d'autorisation sinon mettre 0
$firstConnexion=0; // Après validation du code, génère le 1er refresh token
$refreshToken=1; // Permet de renouveller un token après expiration
$getDailyConsumption=1; //Récupération de la consommation Enedis par jour, ne mettre 1 que si étape firstConnexion ou refreshToken
$getDailyMaxPower=1; //Permet d'avoir l'heure à laquelle la consommation était au maximum
$getLoadCurve=1; // Permet d'avoir la consommation par heure (must have, doit être autorisé au préalable sur le site Enedis)

if(!empty($device_code)){
  cmd::byString("#[Automatismes généraux][EnedisDomoticz][device_code]#")->event(($device_code));
} else {
  $vDevice_code = cmd::byString("#[Automatismes généraux][EnedisDomoticz][device_code]#");
  $device_code = $vDevice_code->execCmd();
}
if(!empty($access_token)){
  cmd::byString("#[Automatismes généraux][EnedisDomoticz][access_token]#")->event(($access_token));
} else {
  $vAccess_token = cmd::byString("#[Automatismes généraux][EnedisDomoticz][access_token]#");
  $access_token = $vAccess_token->execCmd();
}
if(!empty($refresh_token)){
  cmd::byString("#[Automatismes généraux][EnedisDomoticz][refresh_token]#")->event(($refresh_token));
} else {
  $vRefresh_token = cmd::byString("#[Automatismes généraux][EnedisDomoticz][refresh_token]#");
  $refresh_token = $vRefresh_token->execCmd();
  $scenario->setLog("\n Récupération du token : ".$refresh_token. " \n");
}
if(!empty($usage_point_id)){
  cmd::byString("#[Automatismes généraux][EnedisDomoticz][usage_point_id]#")->event(($usage_point_id));
} else {
  $vUsage_point_id = cmd::byString("#[Automatismes généraux][EnedisDomoticz][usage_point_id]#");
  $usage_point_id = $vUsage_point_id->execCmd();  
}

$vToken_type = cmd::byString("#[Automatismes généraux][EnedisDomoticz][token_type]#");
$token_type = $vToken_type->execCmd();  

//Site d'autorisation, gestion des tokens etc...
$CLIENT_ID = array("d198fd52-61c0-4b77-8725-06a1ef90da9f", "9c551777-9d1b-447c-9e68-bfe6896ee002");

$LOGIN_BASE_URI = array('enedis.domoticz.russandol.pro', 'opensrcdev.alwaysdata.net');
$API_ENDPOINT_DEVICE_CODE = array('/device/code', '/domoticzlinkyconnect/device/code');
$API_ENDPOINT_DEVICE_TOKEN = array('/device/token', '/domoticzlinkyconnect/device/token');
$API_ENDPOINT_PROXY = array('/device/proxy', '/domoticzlinkyconnect/device/proxy');
$VERIFY_CODE_URI = array("https://" + $LOGIN_BASE_URI[0] + "/device?code=",
                   "https://" + $LOGIN_BASE_URI[1] + "/domoticzlinkyconnect/device?code=");

//Site d'Enedis
$API_BASE_URI = array("gw.prd.api.enedis.fr", "gw.hml.api.enedis.fr");
$API_ENDPOINT_DATA_CONSUMPTION_LOAD_CURVE = '/v4/metering_data/consumption_load_curve';
$API_ENDPOINT_DATA_CONSUMPTION_MAX_POWER = '/v4/metering_data/daily_consumption_max_power';
$API_ENDPOINT_DATA_DAILY_CONSUMPTION = '/v4/metering_data/daily_consumption';
$API_ENDPOINT_DATA_PRODUCTION_LOAD_CURVE = '/v4/metering_data/production_load_curve';
$API_ENDPOINT_DATA_DAILY_PRODUCTION = '/v4/metering_data/daily_production';

//Initie la variable permettant de relancer le scénario si l'api enedis a été trop appelé
$relaunchScenario = "relaunchScenario";
$scenario->setData($relaunchScenario, '0');

//Obtention du code
if($code){
  $scenario->setLog("Première connexion... \n");
  
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,$LOGIN_BASE_URI[$debug].$API_ENDPOINT_DEVICE_CODE[$debug]);
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_POSTFIELDS, "client_id=".$CLIENT_ID[$debug]);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: DomoticzLinkyPlugin/100.0.0","Content-Type: application/x-www-form-urlencoded","Accept: application/json","Host: ".$LOGIN_BASE_URI[$debug].":443"));
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  
  $scenario->setLog("Appel de ".$LOGIN_BASE_URI[$debug].$API_ENDPOINT_DEVICE_CODE[$debug]." client_id = ".$CLIENT_ID[$debug]);
  
  $result = json_decode(curl_exec($ch),true) or die("Curl Failed\n");

  $scenario->setLog("\n device_code : ".$result['device_code']."\n user_code : ".$result['user_code']."\n url de verification : ".$result['verification_uri']."\n Délai d'expiration (en secondes) : ".$result['expires_in']."\n");
  
  if(empty($device_code) && !empty($result['device_code'])){
    $device_code = $result['device_code'];
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][device_code]#")->event(($result['device_code']));
  }
}

if($firstConnexion){
  $scenario->setLog("Demande de token à ".$LOGIN_BASE_URI[$debug].$API_ENDPOINT_DEVICE_CODE[$debug]." avec le device_id : ".$device_code." \n");
  $scenario->setLog("Paramètre data envoyés : client_id=".$CLIENT_ID[$debug]."&grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=".$device_code_value);
  
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,$LOGIN_BASE_URI[$debug].$API_ENDPOINT_DEVICE_TOKEN[$debug]);
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_POSTFIELDS, "client_id=".$CLIENT_ID[$debug]."&grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=".$device_code);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: DomoticzLinkyPlugin/100.0.0","Content-Type: application/x-www-form-urlencoded","Accept: application/json","Host: ".$LOGIN_BASE_URI[$debug].":443"));
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    
  // Then, after your curl_exec call:
  $response = curl_exec($ch);
  $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  $header = substr($response, 0, $header_size);
  $body = substr($response, $header_size);
  curl_close ($ch);
  
  $scenario->setLog("\n Header ...".$header);
  $scenario->setLog("\n Body ...".$body);
  
  $result = json_decode($response,true) or die("Curl Failed\n");
  $scenario->setLog("\n access_token : ".$result['access_token']."\n token_type : ".$result['token_type']."\n expires_in : ".$result['expires_in']."\n refresh_token : ".$result['refresh_token']."\n usage_point_id : ".$result['usage_point_id']);

  if(!empty($result['access_token'])){
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][access_token]#")->event(($result['access_token']));
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][token_type]#")->event(($result['token_type']));
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][refresh_token]#")->event(($result['refresh_token']));
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][usage_point_id]#")->event(($result['usage_point_id']));
    $access_token = $result['access_token'];
    $token_type = $result['token_type'];
  }
}

if($refreshToken){
  $scenario->setLog("Demande de refresh token à ".$LOGIN_BASE_URI[$debug].$API_ENDPOINT_PROXY[$debug]." \n");
  $scenario->setLog("Avec le data : grant_type=refresh_token&client_id=".$CLIENT_ID[$debug]."&refresh_token=".$refresh_token." \n");  
  
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,$LOGIN_BASE_URI[$debug].$API_ENDPOINT_PROXY[$debug]);
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_POSTFIELDS, "grant_type=refresh_token&client_id=".$CLIENT_ID[$debug]."&refresh_token=".$refresh_token);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: DomoticzLinkyPlugin/100.0.0","Content-Type: application/x-www-form-urlencoded","Accept: application/json","Host: ".$LOGIN_BASE_URI[$debug].":443"));
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  
  // Then, after your curl_exec call:
  $response = curl_exec($ch);
  $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  $header = substr($response, 0, $header_size);
  $body = substr($response, $header_size);
  $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close ($ch);
  	
  $scenario->setLog("\n http_status ...".$http_status);
  $scenario->setLog("\n Header ...".$header);
  $scenario->setLog("\n Body ...".$body);
  
  $result = json_decode($response,true) or die("Curl Failed\n");
  $scenario->setLog("\n Refresh - access_token : ".$result['access_token']."\n token_type : ".$result['token_type']."\n expires_in : ".$result['expires_in']."\n refresh_token : ".$result['refresh_token']."\n usage_point_id : ".$result['usage_point_id']);
  
  if(!empty($result['access_token'])){
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][access_token]#")->event(($result['access_token']));
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][token_type]#")->event(($result['token_type']));
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][refresh_token]#")->event(($result['refresh_token']));
    cmd::byString("#[Automatismes généraux][EnedisDomoticz][usage_point_id]#")->event(($result['usage_point_id']));
    $access_token = $result['access_token'];
    $token_type = $result['token_type'];
  }
}

if($getDailyConsumption){
  $scenario->setLog("Demande de consommation : https://".$API_BASE_URI[$debug].":443".$API_ENDPOINT_DATA_DAILY_CONSUMPTION."?start=".$startDate."&end=".$endDate."&usage_point_id=".$usage_point_id." \n");
  $scenario->setLog("Authorization: ".$token_type." ".$access_token,"Accept: application/json \n");  
  
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,"https://".$API_BASE_URI[$debug].":443".$API_ENDPOINT_DATA_DAILY_CONSUMPTION."?start=".$startDate."&end=".$endDate."&usage_point_id=".$usage_point_id);
  curl_setopt($ch, CURLOPT_HTTP_VERSION,  CURL_HTTP_VERSION_1_1);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: ".$token_type." ".$access_token,"Accept: application/json"));
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,  2);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  
  // Then, after your curl_exec call:
  $response = curl_exec($ch);
  $scenario->setLog("\n Response ...".$response);
  $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  $header = substr($response, 0, $header_size);
  $body = substr($response, $header_size);
  $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close ($ch);
  	
  $scenario->setLog("\n http_status ...".$http_status);
  $scenario->setLog("\n Header ...".$header);
  $scenario->setLog("\n Body ...".$body);
  
  if($http_status == "200"){
    $result = json_decode($response,true) or die("Curl Failed\n");
    $value = cmd::byString("#[Automatismes généraux][EnedisDomoticz][value]#");
    
    foreach($result['meter_reading']['interval_reading'] as $interval_reading){
      //$scenario->setLog("\n value ...".$interval_reading['value']." date ... ".$interval_reading['date']." 12:00:00");
      $value->addHistoryValue($interval_reading['value'], $interval_reading['date']." 12:00:00");
    }
  } else {
    //Relancer le scénario dans 3 heures
    $scenario->setData($relaunchScenario, '1');
  }
}

if($getDailyMaxPower){
  $scenario->setLog("Demande de daily max power : https://".$API_BASE_URI[$debug].":443".$API_ENDPOINT_DATA_CONSUMPTION_MAX_POWER."?start=".$avantHierDate."&end=".$endDate."&usage_point_id=".$usage_point_id." \n");
  $scenario->setLog("Authorization: ".$token_type." ".$access_token,"Accept: application/json \n");  
  
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,"https://".$API_BASE_URI[$debug].":443".$API_ENDPOINT_DATA_CONSUMPTION_MAX_POWER."?start=".$avantHierDate."&end=".$endDate."&usage_point_id=".$usage_point_id);
  curl_setopt($ch, CURLOPT_HTTP_VERSION,  CURL_HTTP_VERSION_1_1);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: ".$token_type." ".$access_token,"Accept: application/json"));
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,  2);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  
  // Then, after your curl_exec call:
  $response = curl_exec($ch);
  $scenario->setLog("\n Response ...".$response);
  $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  $header = substr($response, 0, $header_size);
  $body = substr($response, $header_size);
  $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close ($ch);
  	
  $scenario->setLog("\n http_status ...".$http_status);
  $scenario->setLog("\n Header ...".$header);
  $scenario->setLog("\n Body ...".$body);
  
  if($http_status == "200"){
    $result = json_decode($response,true) or die("Curl Failed\n");
	$value = cmd::byString("#[Automatismes généraux][EnedisDomoticz][maxPower]#");
    
    foreach($result['meter_reading']['interval_reading'] as $interval_reading){
      //$scenario->setLog("\n value ...".$interval_reading['value']." date ... ".$interval_reading['date']);
      $value->addHistoryValue($interval_reading['value'], $interval_reading['date']);
    }
  } else {
    //Relancer le scénario dans 3 heures
    $scenario->setData($relaunchScenario, '1');
  }
}

if($getLoadCurve){
  $scenario->setLog("Demande de consumption load curve : https://".$API_BASE_URI[$debug].":443".$API_ENDPOINT_DATA_CONSUMPTION_LOAD_CURVE."?start=".$startDate."&end=".$endDate."&usage_point_id=".$usage_point_id." \n");
  $scenario->setLog("Authorization: ".$token_type." ".$access_token,"Accept: application/json \n");  
  
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,"https://".$API_BASE_URI[$debug].":443".$API_ENDPOINT_DATA_CONSUMPTION_LOAD_CURVE."?start=".$startDate."&end=".$endDate."&usage_point_id=".$usage_point_id);
  curl_setopt($ch, CURLOPT_HTTP_VERSION,  CURL_HTTP_VERSION_1_1);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: ".$token_type." ".$access_token,"Accept: application/json"));
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,  2);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  
  // Then, after your curl_exec call:
  $response = curl_exec($ch);
  $scenario->setLog("\n Response ...".$response);
  $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  $header = substr($response, 0, $header_size);
  $body = substr($response, $header_size);
  $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close ($ch);
  	
  $scenario->setLog("\n http_status ...".$http_status);
  $scenario->setLog("\n Header ...".$header);
  $scenario->setLog("\n Body ...".$body);
  
  if($http_status == "200"){
    $result = json_decode($response,true) or die("Curl Failed\n");
	$value = cmd::byString("#[Automatismes généraux][EnedisDomoticz][value]#");
    
    foreach($result['meter_reading']['interval_reading'] as $interval_reading){
      //$scenario->setLog("\n value ...".$interval_reading['value']." date ... ".$interval_reading['date']);
      $value->addHistoryValue($interval_reading['value'], $interval_reading['date']);
    }
  } else {
    //Relancer le scénario dans 3 heures
    $scenario->setData($relaunchScenario, '1');
  }
}
Pour la récupération des valeurs et l'exploitation dans un virtuel :

Récupération en KwH de la veille :
statisticsbetween(#[Automatismes généraux][EnedisDomoticz][value]#,sum,2 days ago 00:00, 2 days ago 23:59)/1000

Récupération en KwH de la semaine en cours
statisticsbetween(#[Automatismes généraux][EnedisDomoticz][value]#,sum,previous monday 00:00:00, Today)/1000

Récupération en KwH de la semaine dernière
statisticsbetween(#[Automatismes généraux][EnedisDomoticz][value]#,sum,monday last week 00:00, sunday last week 23:59)/1000

Récupération en KwH du mois en cours
statisticsbetween(#[Automatismes généraux][EnedisDomoticz][value]#,sum,first day of this month 00:00:00, Today)/1000

Récupération en KwH du mois dernier
statisticsbetween(#[Automatismes généraux][EnedisDomoticz][value]#,sum,first day of last month 00:00:00, last day of last month 23:59:59)/1000

Récupération en KwH de l'année en cours
statisticsbetween(#[Automatismes généraux][EnedisDomoticz][value]#,sum,first day of january this year 00:00:00, Today)/1000

Cordialement,

Si jamais quelqu’un sait déjà comment faire, ca m’évitera des recherches.

Réponse en json :

"interval_reading": [
            {
                "value": "19403",
                "date": "2020-09-15"
            },
            {
                "value": "15043",
                "date": "2020-09-16"
            },
            {
                "value": "23884",
                "date": "2020-09-17"
            },
            {
                "value": "17452",
                "date": "2020-09-18"
            }
        ]

Il faudrait que j’historise la valeur à la bonne date et si jamais une requête est faite 2 fois, que ca écrase l’ancienne valeur (ou que ca ne fasse rien, le but étant d’avoir 2 valeurs historisées identiques sur la même date).
Le tout pour faire simple depuis un scénario sur une commande info d’un virtuel par exemple.

EDIT : Bon a priori cette info devrait faire le job

1 « J'aime »

Scénario terminé, me PM si des interessés.
Voilà ce que ca donne avec un virtuel qui me calcul les tarifs et un graphe de suivi de consommation toutes les 30 mins :

1 « J'aime »

Bonjour,

Pourquoi partager en Mp et non directement dans le topic ?

Hello,
Car j’utilise un api d’oauth utilisé par un plugin dom@tic Z, j’ai grosso modo reconvertit en php et modifié leur script pour qu’il marche pour jeedom.
Je n’ai pas envie qu’ils rajoutent des restrictions s’ils voient que tout les users Jeedom l’utilisent.
Cordialement

Si jamais quelqu’un est intéressé pour faire un plugin pour les particuliers et mettre à disposition un serveur oauth, me dire également je peux aider en fournissant le scénario côté utilisateurs qui appele l’api enedis : Data Connect – Enedis DataHub

@Loic, une chance que ce soit fait en officiel ?

Bonjour,
C’est fait juste on ne la pas encore sortie ni tester. On est completement sous l’eau donc je peux pas dire si ca sera dan 1 mois ou 1ans…

1 « J'aime »

Bonjour à tous,

J’ai finalement partagé le scénario qui fonctionne avec les particuliers et une méthode d’exploitation dans un virtuel pour récupérer les valeurs en KwH

CF mon 1er post.

Cordialement,