diff --git a/teleinfo.py b/teleinfo.py old mode 100644 new mode 100755 index 8003ce8..a05f415 --- a/teleinfo.py +++ b/teleinfo.py @@ -4,23 +4,6 @@ # __licence__ = "Apache License 2.0" # Python 3, prérequis : pip install pySerial influxdb -# -# Exemple de trame: -# { -# 'BASE': '123456789' # Index heure de base en Wh -# 'OPTARIF': 'HC..', # Option tarifaire HC/BASE -# 'IMAX': '007', # Intensité max -# 'HCHC': '040177099', # Index heure creuse en Wh -# 'IINST': '005', # Intensité instantanée en A -# 'PAPP': '01289', # Puissance Apparente, en VA -# 'MOTDETAT': '000000', # Mot d'état du compteur -# 'HHPHC': 'A', # Horaire Heures Pleines Heures Creuses -# 'ISOUSC': '45', # Intensité souscrite en A -# 'ADCO': '000000000000', # Adresse du compteur -# 'HCHP': '035972694', # index heure pleine en Wh -# 'PTEC': 'HP..' # Période tarifaire en cours -# } - import logging import time @@ -30,16 +13,48 @@ import requests import serial from influxdb import InfluxDBClient +# Nombre de secondes entre deux transmission des mesures +DELAY_MESURE = 10 + +# clés à utiliser - les autres ne seront pas transmises +USED_MESURE_KEYS = [ + 'LTARF', + 'EAST', + 'EASF01', + 'EASF02', + 'IRMS1', + 'URMS1', + 'SINSTS', + 'SMAXSN', + 'CCASN', + 'UMOY1', + 'MSG1', + 'NTARF', +] # clés téléinfo -INT_MESURE_KEYS = ['BASE', 'IMAX', 'HCHC', 'IINST', 'PAPP', 'ISOUSC', 'ADCO', 'HCHP'] +INT_MESURE_KEYS = [ + 'EAST', + 'EASF', + 'EASD', + 'EAIT', + 'ERQ', + 'IRMS', + 'URMS', + 'PREF', + 'PCOUP', + 'SINST', + 'SMAX', + 'CCA', + 'UMOY', +] # création du logguer -logging.basicConfig(filename='/var/log/teleinfo/releve.log', level=logging.INFO, format='%(asctime)s %(message)s') +logging.basicConfig(filename='/tmp/teleinfo-releve.log', level=logging.INFO, format='%(asctime)s %(message)s') logging.info("Teleinfo starting..") # connexion a la base de données InfluxDB -client = InfluxDBClient('localhost', 8086) +client = InfluxDBClient('192.168.0.10', 8086) DB_NAME = "teleinfo" connected = False while not connected: @@ -58,9 +73,9 @@ while not connected: connected = True -def add_measures(measures, time_measure): +def add_measures(measures): points = [] - for measure, value in measures.items(): + for measure, values in measures.items(): point = { "measurement": measure, "tags": { @@ -69,12 +84,9 @@ def add_measures(measures, time_measure): "region": "linky" }, "time": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), - "fields": { - "value": value - } + "fields": values } points.append(point) - client.write_points(points) @@ -85,13 +97,13 @@ def verif_checksum(data, checksum): sum_unicode = (data_unicode & 63) + 32 return (checksum == chr(sum_unicode)) +def checksum(line:str) -> str: + return chr((sum(list(line)) & 0x3F) + 0x20) def main(): - with serial.Serial(port='/dev/ttyS0', baudrate=1200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, - bytesize=serial.SEVENBITS, timeout=1) as ser: - - logging.info("Teleinfo is reading on /dev/ttyS0..") + with serial.Serial(port='/dev/ttyAMA0', baudrate=9600, parity=serial.PARITY_EVEN, bytesize=serial.SEVENBITS, timeout=1) as ser: + logging.info("Teleinfo is reading on /dev/ttyAMA0..") trame = dict() # boucle pour partir sur un début de trame @@ -99,41 +111,50 @@ def main(): while b'\x02' not in line: # recherche du caractère de début de trame line = ser.readline() - # lecture de la première ligne de la première trame - line = ser.readline() - + delaycounter = DELAY_MESURE while True: - line_str = line.decode("utf-8") + while delaycounter < DELAY_MESURE: + ser.read_until(b'\x03') + delaycounter+=1 + line = ser.readline() logging.debug(line) - try: - # separation sur espace /!\ attention le caractere de controle 0x32 est un espace aussi - [key, val, *_] = line_str.split(" ") - - # supprimer les retours charriot et saut de ligne puis selectionne le caractere - # de controle en partant de la fin - checksum = (line_str.replace('\x03\x02', ''))[-3:-2] - - if verif_checksum(f"{key} {val}", checksum): - # creation du champ pour la trame en cours avec cast des valeurs de mesure en "integer" - trame[key] = int(val) if key in INT_MESURE_KEYS else val - + dataset = line.replace(b'\x03\x02', b'').rstrip(b'\r\n') + checksumchar = checksum(dataset[:-1]) + if checksumchar == chr(dataset[-1]): + spline = dataset.split(b'\t') + logging.debug(spline) + etiquette = spline[0].decode('ascii') + if etiquette in USED_MESURE_KEYS: + value = spline[1].decode('ascii') + timestamp = None + if len(spline) == 4 and spline[2] != b'' : # Horodaté + value = spline[2].decode('ascii') + timestamp = spline[1].decode('ascii') + for radix in INT_MESURE_KEYS: + if etiquette.startswith(radix): + value = int(value) + trame[etiquette] = { + "value": value, + "timestamp": timestamp, + } + else: + logging.debug('Checksum error, aborting frame') if b'\x03' in line: # si caractère de fin dans la ligne, on insère la trame dans influx - del trame['ADCO'] # adresse du compteur : confidentiel! time_measure = time.time() # insertion dans influxdb - add_measures(trame, time_measure) + add_measures(trame) # ajout timestamp pour debugger trame["timestamp"] = int(time_measure) logging.debug(trame) trame = dict() # on repart sur une nouvelle trame + delaycounter=0 except Exception as e: logging.error("Exception : %s" % e, exc_info=True) - logging.error("%s %s" % (key, val)) - line = ser.readline() + logging.error("%s" % (line)) if __name__ == '__main__':