Pourquoi les courbes de charge Linky transforment vos études
Sans données de consommation réelles, les bureaux d'études utilisent des profils types (profil résidentiel H00, profil C5 tertiaire…) publiés par RTE. Ces profils sont des moyennes nationales qui peuvent s'écarter de 20% à 40% de la consommation réelle d'un site industriel ou agricole.
Avec les données Linky, vous obtenez jusqu'à 35 000 points de mesure par client (mesures pas-d'heure sur 4 ans), ce qui permet de calculer :
- Le taux d'autoconsommation réel (vs. estimé avec profil type)
- Les pics de consommation et leur concordance avec la production PV
- Le gain d'une batterie de stockage (courbe résiduelle après autoconsommation)
- L'impact des tarifs TEMPO/EJP sur la rentabilité
Les données Linky sont des données personnelles. L'API Data Connect impose que le titulaire du compteur donne son consentement explicite via le portail Enedis avant tout accès. Ce consentement est valable 3 ans et peut être révoqué à tout moment.
Inscription à Enedis Data Connect (Portail Développeurs)
La plateforme est accessible sur datahub-enedis.fr. Le processus d'inscription pour une entreprise prend généralement 5 à 10 jours ouvrables.
- Créer un compte sur
datahub-enedis.fr - Choisir le périmètre : Données de mesure (courbes de charge) + Données contractuelles (puissance souscrite)
- Déclarer votre URL de callback OAuth2
- Obtenir votre
client_idetclient_secret - Tester sur le sandbox avant la mise en production
Flux OAuth2 complet
Data Connect utilise OAuth2 Authorization Code Flow. Le client (titulaire du compteur) doit approuver la demande d'accès sur le portail Enedis.
import requests
import urllib.parse
import secrets
import base64
# ── Configuration ─────────────────────────────────────────────────────────────
ENEDIS_AUTH_URL = "https://mon-compte-particulier.enedis.fr/dataconnect/v1/oauth2/authorize"
ENEDIS_TOKEN_URL = "https://mon-compte-particulier.enedis.fr/dataconnect/v1/oauth2/token"
ENEDIS_API_BASE = "https://ext.api.lincsesdc.cdc.enedis.fr/sg"
CLIENT_ID = "votre_client_id" # À remplacer par votre identifiant
CLIENT_SECRET = "votre_client_secret" # À stocker en variable d'environnement !
REDIRECT_URI = "https://votre-app.fr/callback"
def construire_url_autorisation(usage_point_id: str = None) -> tuple[str, str]:
"""
Génère l'URL de redirection vers Enedis pour le consentement.
Retourne (url, state) – conserver le state pour validation CSRF.
"""
state = secrets.token_urlsafe(32)
params = {
"client_id": CLIENT_ID,
"response_type": "code",
"redirect_uri": REDIRECT_URI,
"state": state,
"duration": "P3Y", # Durée du consentement : 3 ans (ISO 8601)
}
if usage_point_id:
# Pré-remplir le numéro de compteur si connu
params["usage_point_id"] = usage_point_id
url = ENEDIS_AUTH_URL + "?" + urllib.parse.urlencode(params)
return url, state
def echanger_code_contre_token(code: str) -> dict:
"""
Échange le code d'autorisation (callback) contre un access_token.
Utilise l'authentification Basic (client_id:client_secret en Base64).
"""
credentials = f"{CLIENT_ID}:{CLIENT_SECRET}"
b64 = base64.b64encode(credentials.encode()).decode()
headers = {
"Authorization": f"Basic {b64}",
"Content-Type": "application/x-www-form-urlencoded",
}
body = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI,
}
resp = requests.post(ENEDIS_TOKEN_URL, headers=headers, data=body, timeout=15)
resp.raise_for_status()
return resp.json()
# Retourne : {'access_token': ..., 'refresh_token': ..., 'expires_in': 3600, ...}
Récupération des courbes de charge (pas 30 min)
from datetime import date, timedelta
import pandas as pd
def recuperer_courbe_charge(
access_token: str,
usage_point_id: str,
date_debut: date,
date_fin: date,
) -> pd.DataFrame:
"""
Récupère la courbe de charge de consommation (intervalles 30 min).
Returns:
DataFrame avec colonnes : timestamp (UTC), wh (énergie Wh par pas),
w_moyen (puissance moyenne W par pas)
"""
url = f"{ENEDIS_API_BASE}/metering_data/consumption_load_curve/v5"
params = {
"usage_point_id": usage_point_id,
"start": date_debut.isoformat(),
"end": date_fin.isoformat(),
}
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/json",
}
resp = requests.get(url, params=params, headers=headers, timeout=30)
resp.raise_for_status()
data = resp.json()
# Parsing de la réponse Enedis
intervals = (data
.get("meter_reading", {})
.get("interval_reading"span>, [])
)
rows = []
for iv in intervals:
rows.append({
"timestamp": pd.to_datetime(iv["date"], utc=True),
"wh": float(iv["value"]),
})
df = pd.DataFrame(rows).set_index("timestamp").sort_index()
df["w_moyen"] = df["wh"] * 2 # Wh/30min → W moyen (×2 car ½ heure)
return df
Calculer le taux d'autoconsommation avec la courbe réelle
import numpy as np
import pandas as pd
def calculer_autoconsommation(
df_consommation: pd.DataFrame, # courbe Enedis (colonne 'wh', index UTC 30 min)
df_production: pd.DataFrame, # production PVGIS ou simulée (colonne 'wh', index UTC 30 min)
) -> dict:
"""
Croise production PV et consommation réelle pour calculer :
- Taux d'autoconsommation (% production autoconsommée)
- Taux d'autosuffisance (% consommation couverte par PV)
- Surplus injecté (kWh/an)
- Économies annuelles estimées (€)
"""
# Aligner les deux séries sur le même index temporel
df = pd.DataFrame({
"conso_wh": df_consommation["wh"],
"prod_wh": df_production["wh"],
}).dropna()
# Pour chaque pas de temps, autoconsommé = min(prod, conso)
df["autoconso_wh"] = np.minimum(df["prod_wh"], df["conso_wh"])
df["surplus_wh"] = (df["prod_wh"] - df["conso_wh"]).clip(lower=0)
total_prod = df["prod_wh"].sum() # Wh
total_conso = df["conso_wh"].sum()
total_auto = df["autoconso_wh"].sum()
total_surplus = df["surplus_wh"].sum()
taux_autoconsommation = (total_auto / total_prod * 100) if total_prod > 0 else 0
taux_autosuffisance = (total_auto / total_conso * 100) if total_conso > 0 else 0
return {
"production_kwh_an": round(total_prod / 1000, 0),
"consommation_kwh_an": round(total_conso / 1000, 0),
"autoconsommation_kwh_an": round(total_auto / 1000, 0),
"surplus_injecte_kwh_an": round(total_surplus / 1000, 0),
"taux_autoconsommation_pct": round(taux_autoconsommation, 1),
"taux_autosuffisance_pct": round(taux_autosuffisance, 1),
}
Les données Linky sont au pas 30 min, mais PVGIS fournit des données horaires (pas 1h).
Avant de croiser les deux, ré-échantillonnez les données Linky à 1h avec
df.resample('1H').sum() pour garantir la cohérence des calculs.
Sécurité : ne jamais exposer vos tokens
- Stockez
client_secretetrefresh_tokenuniquement côté serveur (variable d'environnement ou coffre-fort secrets) - N'exposez jamais l'
access_tokenen JavaScript front-end (XSS) - Validez toujours le paramètre
stateau retour du callback pour prévenir les attaques CSRF - Les tokens expirent en 3 600 s — implémentez un mécanisme de refresh automatique
HeliaPV intègre Enedis Data Connect nativement
Importez les courbes de charge de vos clients directement dans l'interface. HeliaPV calcule automatiquement le taux d'autoconsommation sur 6 tarifications différentes (BASE, HPHC, TEMPO, EJP, C4…) et les intègre dans la proposition PDF.
Essai gratuit – 50 analyses offertes Voir les fonctionnalités BET