En attendant que quelqu’un ait le temps d’adapter le plugin aux lubies de GRDF, j’ai rapidement adapté le script initialement fait pour Domoticz (cf plus haut). C’est sale et fait dans l’urgence, mais ça fonctionne (enfin, quand le site de GRDF fonctionne). Aussi, si ça peut dépanner quelqu’un en attendant mieux (ne comptez pas sur moi, je n’ai absolument pas le temps de me pencher sur le sujet)…
Script python à faire fonctionner grâce au plugin script (remplacez userName et password par vos identifiants chez GRDF). Modifiez nbDaysImported si 10 jours d’historique ne vous conviennent pas. Le script génère un fichier XML contenant les données. Ne reste plus qu’à exploiter ces données avec un scenario pour alimenter un virtuel et faire ce que vous voulez avec…
#!/usr/bin/env python3.7
# -*- coding: utf-8 -*-
# (C) v1.0 2021-11-29 Scrat
# (C) v1.2.2 2021-12-04 Scrat
# - Fix : Bad login is not detected #8
# - Fix : Log file will grow indefinitely #7
#
# gazpar.script_python - Récupère les données de consommation sur le site de GRDF
#
# Source : https://github.com/Scrat95220/DomoticzGazpar
# 2021-12-04 - Adaptation pour utilisation avec le plugin Script de Jeedom
# 2021-12-05 - Ajout des corrections de la version v1.2.2
# Note : avant la première utilisation, s'assurer que tous les packages
# nécessaires sont bien installés =>
# apt-get install python3 python3-dateutil python3-requests
#
# Note 2 : !! IMPORTANT !! Pour que le plugin script de Jeedom utilise python3,
# le nom de fichier ne doit pas se terminer par .py ou .python ou
# quoique ce soit contenant l'extension .python (même un
# .python_quelque_chose). Sinon c'est python2 qui est exécuté
"""Generates energy consumption JSON files from GRDf consumption data
collected via their website (API).
"""
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import configparser
import os
import requests
import datetime
import logging
import sys
import json
from dateutil.relativedelta import relativedelta
# ---
# URLs
LOGIN_BASE_URI = 'https://login.monespace.grdf.fr/sofit-account-api/api/v1/auth'
API_BASE_URI = 'https://monespace.grdf.fr/'
# ---
# Initialisation des données utilisateurs
userName = ""
password = ""
#
# Nombre de jours de l'historique à importer. Max 1096
nbDaysImported = 10
# ---
script_dir=os.path.dirname(os.path.realpath(__file__)) + os.path.sep
# ---
class GazparServiceException(Exception):
"""Thrown when the webservice threw an exception."""
pass
# ------------
# Date formatting
def dtostr(date):
return date.strftime("%Y-%m-%d")
# ----------
def login():
"""Logs the user into the GRDF API.
"""
session = requests.Session()
payload = {
'email': userName,
'password': password,
'goto':'https://sofa-connexion.grdf.fr:443/openam/oauth2/externeGrdf/authorize?response_type=code%26scope=openid%20profile%20email%20infotravaux%20%2Fv1%2Faccreditation%20%2Fv1%2Faccreditations%20%2Fdigiconso%2Fv1%20%2Fdigiconso%2Fv1%2Fconsommations%20new_meg%20%2FDemande.read%20%2FDemande.write%26client_id=prod_espaceclient%26state=0%26redirect_uri=https%3A%2F%2Fmonespace.grdf.fr%2F_codexch%26nonce=7cV89oGyWnw28DYdI-702Gjy9f5XdIJ_4dKE_hbsvag%26by_pass_okta=1%26capp=meg',
'capp':'meg'
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Referer': 'https://login.monespace.grdf.fr/mire/connexion?goto=https:%2F%2Fsofa-connexion.grdf.fr:443%2Fopenam%2Foauth2%2FexterneGrdf%2Fauthorize%3Fresponse_type%3Dcode%26scope%3Dopenid%2520profile%2520email%2520infotravaux%2520%252Fv1%252Faccreditation%2520%252Fv1%252Faccreditations%2520%252Fdigiconso%252Fv1%2520%252Fdigiconso%252Fv1%252Fconsommations%2520new_meg%2520%252FDemande.read%2520%252FDemande.write%26client_id%3Dprod_espaceclient%26state%3D0%26redirect_uri%3Dhttps%253A%252F%252Fmonespace.grdf.fr%252F_codexch%26nonce%3D7cV89oGyWnw28DYdI-702Gjy9f5XdIJ_4dKE_hbsvag%26by_pass_okta%3D1%26capp%3Dmeg&realm=%2FexterneGrdf&capp=meg'
}
resp1 = session.post(LOGIN_BASE_URI, data=payload, headers=headers)
#print (resp1.text)
if resp1.status_code != requests.codes.ok:
print("Login call - error status :"+resp1.status_code+'\n');
logging.error("Login call - error status :"+resp1.status_code+'\n')
exit()
j = json.loads(resp1.text)
if j['state'] != "SUCCESS":
print("Login call - error status :"+j['state']+'\n');
logging.error("Login call - error status :"+j['state']+'\n')
exit()
#2nd request
headers = {
'Referer': 'https://sofa-connexion.grdf.fr:443/openam/oauth2/externeGrdf/authorize?response_type=code&scope=openid profile email infotravaux /v1/accreditation /v1/accreditations /digiconso/v1 /digiconso/v1/consommations new_meg /Demande.read /Demande.write&client_id=prod_espaceclient&state=0&redirect_uri=https://monespace.grdf.fr/_codexch&nonce=7cV89oGyWnw28DYdI-702Gjy9f5XdIJ_4dKE_hbsvag&by_pass_okta=1&capp=meg'
}
resp2 = session.get(API_BASE_URI, allow_redirects=True)
if resp2.status_code != requests.codes.ok:
print("Login 2nd call - error status :"+resp2.status_code+'\n');
logging.error("Login 2nd call - error status :"+resp2.status_code+'\n')
exit()
return session
# ----------
def generate_xml(session, start_date, end_date):
"""Retreives monthly energy consumption data."""
#print('start_date: ' + start_date)
#print('end_date: ' + end_date)
#3nd request- Get NumPCE
resp3 = session.get('https://monespace.grdf.fr/api/e-connexion/users/pce/historique-consultation')
if resp3.status_code != requests.codes.ok:
print("Get NumPce call - error status :",resp3.status_code, '\n');
logging.error("Get NumPce call - error status :",resp3.status_code, '\n')
exit()
#print(resp3.text)
v_JSON_DATA = json.loads(resp3.text)
numPce = v_JSON_DATA[0]['numPce']
v_DATA = get_data_with_interval(session, 'Mois', numPce, start_date, end_date)
v_JSON_DATA = json.loads(v_DATA)
# ---
# Création du début du contenu du fichier XML
v_CONTENT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
v_CONTENT = v_CONTENT + "<ITEMS>\n"
v_CONTENT = v_CONTENT + " <TIME>" + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "</TIME>\n"
# Extraction de l'historique des données
for v_ITEM in v_JSON_DATA[str(numPce)]['releves']:
v_DATE_RELEVE = v_ITEM['journeeGaziere']
v_KWH = v_ITEM['energieConsomme']
v_VOLUME = v_ITEM['volumeBrutConsomme']
v_INDEX_M3_START = v_ITEM['indexDebut']
v_INDEX_M3_END = v_ITEM['indexFin']
v_CONTENT = v_CONTENT + " <RELEVE>\n"
v_CONTENT = v_CONTENT + " <DATE_RELEVE>" + v_DATE_RELEVE + "</DATE_RELEVE>\n"
v_CONTENT = v_CONTENT + " <KWH>" + str(v_KWH) + "</KWH>\n"
v_CONTENT = v_CONTENT + " <VOLUME>" + str(v_VOLUME) + "</VOLUME>\n"
v_CONTENT = v_CONTENT + " <INDEX_M3_START>" + str(v_INDEX_M3_START) + "</INDEX_M3_START>\n"
v_CONTENT = v_CONTENT + " <INDEX_M3_END>" + str(v_INDEX_M3_END) + "</INDEX_M3_END>\n"
v_CONTENT = v_CONTENT + " </RELEVE>\n"
v_CONTENT = v_CONTENT + "</ITEMS>\n"
file = open(script_dir + "gazpar.xml", "w")
file.write(v_CONTENT)
file.close()
logging.info("Fichier XML écrit")
# ----------
def get_data_with_interval(session, resource_id, numPce, start_date=None, end_date=None):
r=session.get('https://monespace.grdf.fr/api/e-conso/pce/consommation/informatives?dateDebut='+ start_date + '&dateFin=' + end_date + '&pceList[]=' + str(numPce))
if r.status_code != requests.codes.ok:
print("Get data - error status :"+r.status_code+'\n');
logging.error("Get data - error status :",r.status_code, '\n')
exit()
return r.text
# ----------
# Main script
def main():
#logging.basicConfig(filename=script_dir + '/domoticz_gazpar.log', format='%(asctime)s %(message)s', filemode='w', level=logging.INFO)
logging.basicConfig(filename=script_dir + '/gazpar.log', format='%(asctime)s %(message)s', filemode='w', level=logging.INFO)
try:
# Importation des données utilisateurs et de la plage temporelle des données à récupérer
#logging.info("Get configuration")
#get_config()
# Connection au site de GRDF
logging.info("logging in as %s...", userName)
token = login()
logging.info("logged in successfully!")
today = datetime.date.today()
# Génération du XML
logging.info("retrieving data...")
generate_xml(token, dtostr(today - relativedelta(days=int(nbDaysImported))), dtostr(today))
logging.info("got data!")
except GazparServiceException as exc:
logging.error(exc)
sys.exit(1)
if __name__ == "__main__":
main()
Pour exploiter le fichier XML, vous pouvez vous inspirer de ce scénario (naturellement, il faut le virtuel qui va bien) :
# Importe les données d'un compteur Gazpar dans Jeedom.
# Ces données somt récupérées auprès de GRDF par le script '/var/www/html/plugins/script/data/gazpar/gazpar/gazpar.script_python' activé par le plugin Script
#
# 2021-12-05 - Création
# ----------
# Où trouver les données du compteur Gazpar
$FILE = '/var/www/html/plugins/script/data/gazpar/gazpar.xml';
$INDEX = 0;
# -------------
if ( file_exists($FILE) ) {
$XML = simplexml_load_file($FILE);
unset($FILE);
## Récupération des données du virtuel "compteur gazpar - virtuel"
#
# Identification de "compteur-gazpar - virtuel -> date dernier relevé"
$VIRTUEL_DATE_DERNIER_RELEVE_ID = 1;
$VIRTUEL_DATE_DERNIER_RELEVE = cmd::byId($VIRTUEL_DATE_DERNIER_RELEVE_ID)->execCmd();
$VIRTUEL_DATE_DERNIER_RELEVE_TIMESTAMP = strtotime($VIRTUEL_DATE_DERNIER_RELEVE);
#
# Identification de la valeur du virtuel "compteur gazpar - virtuel -> index"
$VIRTUEL_INDEX_ID = 2;
$VIRTUEL_INDEX = cmd::byId($VIRTUEL_INDEX_ID)->execCmd();
#
# Identification de la valeur du virtuel "compteur gazpar - virtuel -> consommation kwh"
$VIRTUEL_KWH_ID = 3;
$VIRTUEL_KWH = cmd::byId($VIRTUEL_KWH_ID)->execCmd();
#
# Identification de la valeur du virtuel "compteur gazpar - virtuel -> volume"
$VIRTUEL_VOLUME_ID = 4;
$VIRTUEL_VOLUME = cmd::byId($VIRTUEL_VOLUME_ID)->execCmd();
#
# Identification de la valeur du virtuel "compteur gazpar - virtuel -> index m3 start"
$VIRTUEL_INDEX_M3_START_ID = 5;
$VIRTUEL_INDEX_M3_START = cmd::byId($VIRTUEL_INDEX_M3_START_ID)->execCmd();
#
# Identification de la valeur du virtuel "compteur gazpar - virtuel -> index m3 end"
$VIRTUEL_INDEX_M3_END_ID = 6;
$VIRTUEL_INDEX_M3_END = cmd::byId($VIRTUEL_INDEX_M3_END_ID)->execCmd();
#
# Identification de la valeur du virtuel "compteur gazpar - virtuel -> coefficient conversion"
$VIRTUEL_COEFF_CONVERSION_ID = 7;
$VIRTUEL_COEFF_CONVERSION = cmd::byId($VIRTUEL_COEFF_CONVERSION_ID)->execCmd();
# ---
foreach ( $XML->RELEVE as $ELEMENT ) {
$GRDF_DATE_RELEVE = (string)$ELEMENT->DATE_RELEVE;
$GRDF_DATE_RELEVE_TIMESTAMP = strtotime($DATE_RELEVE);
$GRDF_INDEX_M3_START = (string)$ELEMENT->INDEX_M3_START;
$GRDF_INDEX_M3_END = (string)$ELEMENT->INDEX_M3_END;
$GRDF_KWH = (string)$ELEMENT->KWH;
$GRDF_VOLUME = (string)$ELEMENT->VOLUME;
if ( $GRDF_KWH > 0 && $GRDF_VOLUME > 0 ) {
$GRDF_COEFFICIENT_CONVERSION = round($GRDF_KWH / $GRDF_VOLUME, 2);
} else {
$GRDF_COEFFICIENT_CONVERSION = 0;
}
# ---
# Mise à jour de l'historique (supposé vide ou ne contenant qu'une seule valeur)
$DATE_DEBUT = $GRDF_DATE_RELEVE . ' 00:00:00';
$DATE_FIN = $GRDF_DATE_RELEVE . ' 23:59:59';
$DATE_HEURE_RELEVE = $GRDF_DATE_RELEVE . ' 07:00:00';
#
# kWh
$VALEUR = history::getStatistique($VIRTUEL_KWH_ID, $DATE_DEBUT, $DATE_FIN)["max"];
if ( ! isset($VALEUR) || empty($VALEUR) ) {
$CMD = cmd::byID($VIRTUEL_KWH_ID);
$CMD->addHistoryValue($GRDF_KWH, $DATE_HEURE_RELEVE);
}
#
# Index
$VALEUR = history::getStatistique($VIRTUEL_INDEX_ID, $DATE_DEBUT, $DATE_FIN)["max"];
if ( ! isset($VALEUR) || empty($VALEUR) ) {
$CMD = cmd::byID($VIRTUEL_INDEX_ID);
$INDEX = $INDEX + $GRDF_KWH;
$CMD->addHistoryValue($INDEX, $DATE_HEURE_RELEVE);
} else {
$INDEX = $VALEUR;
}
#
# Volume
$VALEUR = history::getStatistique($VIRTUEL_VOLUME_ID, $DATE_DEBUT, $DATE_FIN)["max"];
if ( ! isset($VALEUR) || empty($VALEUR) ) {
$CMD = cmd::byID($VIRTUEL_VOLUME_ID);
$CMD->addHistoryValue($GRDF_VOLUME, $DATE_HEURE_RELEVE);
}
#
# Index M3 Start
$VALEUR = history::getStatistique($VIRTUEL_INDEX_M3_START_ID, $DATE_DEBUT, $DATE_FIN)["max"];
if ( ! isset($VALEUR) || empty($VALEUR) ) {
$CMD = cmd::byID($VIRTUEL_INDEX_M3_START_ID);
$CMD->addHistoryValue($GRDF_INDEX_M3_START, $DATE_HEURE_RELEVE);
}
#
# Index M3 End
$VALEUR = history::getStatistique($VIRTUEL_INDEX_M3_END_ID, $DATE_DEBUT, $DATE_FIN)["max"];
if ( ! isset($VALEUR) || empty($VALEUR) ) {
$CMD = cmd::byID($VIRTUEL_INDEX_M3_END_ID);
$CMD->addHistoryValue($GRDF_INDEX_M3_END, $DATE_HEURE_RELEVE);
}
#
# Coefficient de convertion
$VALEUR = history::getStatistique($VIRTUEL_COEFF_CONVERSION_ID, $DATE_DEBUT, $DATE_FIN)["max"];
if ( ! isset($VALEUR) || empty($VALEUR) ) {
$CMD = cmd::byID($VIRTUEL_COEFF_CONVERSION_ID);
$CMD->addHistoryValue($GRDF_COEFFICIENT_CONVERSION, $DATE_HEURE_RELEVE);
}
# ---
# Mise à jour du Virtuel
# À gérer plus tard -> if ( $DATE_RELEVE_TIMESTAMP > $VIRTUEL_DATE_DERNIER_RELEVE_TIMESTAMP ) {
cmd::byId($VIRTUEL_DATE_DERNIER_RELEVE_ID)->event($DATE_HEURE_RELEVE);
cmd::byId($VIRTUEL_KWH_ID)->event($GRDF_KWH, $DATE_HEURE_RELEVE);
cmd::byID($VIRTUEL_VOLUME_ID)->event($GRDF_VOLUME, $DATE_HEURE_RELEVE);
cmd::byID($VIRTUEL_INDEX_ID)->event($INDEX, $DATE_HEURE_RELEVE);
cmd::byID($VIRTUEL_INDEX_M3_START_ID)->event($GRDF_INDEX_M3_START, $DATE_HEURE_RELEVE);
cmd::byID($VIRTUEL_INDEX_M3_END_ID)->event($GRDF_INDEX_M3_END, $DATE_HEURE_RELEVE);
cmd::byID($VIRTUEL_COEFF_CONVERSION_ID)->event($GRDF_COEFFICIENT_CONVERSION, $DATE_HEURE_RELEVE); }
#}
unset($XML);
}
PS oui, mon code est dégueux. Pas le temps de faire mieux.