This commit is contained in:
Gitouche 2023-01-02 13:17:07 +01:00
parent 2dc277e8a4
commit 419d29f5be

121
teleinfo.py Normal file → Executable file
View file

@ -4,23 +4,6 @@
# __licence__ = "Apache License 2.0" # __licence__ = "Apache License 2.0"
# Python 3, prérequis : pip install pySerial influxdb # 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 logging
import time import time
@ -30,16 +13,48 @@ import requests
import serial import serial
from influxdb import InfluxDBClient 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 # 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 # 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..") logging.info("Teleinfo starting..")
# connexion a la base de données InfluxDB # connexion a la base de données InfluxDB
client = InfluxDBClient('localhost', 8086) client = InfluxDBClient('192.168.0.10', 8086)
DB_NAME = "teleinfo" DB_NAME = "teleinfo"
connected = False connected = False
while not connected: while not connected:
@ -58,9 +73,9 @@ while not connected:
connected = True connected = True
def add_measures(measures, time_measure): def add_measures(measures):
points = [] points = []
for measure, value in measures.items(): for measure, values in measures.items():
point = { point = {
"measurement": measure, "measurement": measure,
"tags": { "tags": {
@ -69,12 +84,9 @@ def add_measures(measures, time_measure):
"region": "linky" "region": "linky"
}, },
"time": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), "time": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
"fields": { "fields": values
"value": value
}
} }
points.append(point) points.append(point)
client.write_points(points) client.write_points(points)
@ -85,13 +97,13 @@ def verif_checksum(data, checksum):
sum_unicode = (data_unicode & 63) + 32 sum_unicode = (data_unicode & 63) + 32
return (checksum == chr(sum_unicode)) return (checksum == chr(sum_unicode))
def checksum(line:str) -> str:
return chr((sum(list(line)) & 0x3F) + 0x20)
def main(): def main():
with serial.Serial(port='/dev/ttyS0', baudrate=1200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, with serial.Serial(port='/dev/ttyAMA0', baudrate=9600, parity=serial.PARITY_EVEN, bytesize=serial.SEVENBITS, timeout=1) as ser:
bytesize=serial.SEVENBITS, timeout=1) as ser:
logging.info("Teleinfo is reading on /dev/ttyS0..")
logging.info("Teleinfo is reading on /dev/ttyAMA0..")
trame = dict() trame = dict()
# boucle pour partir sur un début de trame # 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 while b'\x02' not in line: # recherche du caractère de début de trame
line = ser.readline() line = ser.readline()
# lecture de la première ligne de la première trame delaycounter = DELAY_MESURE
line = ser.readline()
while True: 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) logging.debug(line)
try: try:
# separation sur espace /!\ attention le caractere de controle 0x32 est un espace aussi dataset = line.replace(b'\x03\x02', b'').rstrip(b'\r\n')
[key, val, *_] = line_str.split(" ") checksumchar = checksum(dataset[:-1])
if checksumchar == chr(dataset[-1]):
# supprimer les retours charriot et saut de ligne puis selectionne le caractere spline = dataset.split(b'\t')
# de controle en partant de la fin logging.debug(spline)
checksum = (line_str.replace('\x03\x02', ''))[-3:-2] etiquette = spline[0].decode('ascii')
if etiquette in USED_MESURE_KEYS:
if verif_checksum(f"{key} {val}", checksum): value = spline[1].decode('ascii')
# creation du champ pour la trame en cours avec cast des valeurs de mesure en "integer" timestamp = None
trame[key] = int(val) if key in INT_MESURE_KEYS else val 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 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() time_measure = time.time()
# insertion dans influxdb # insertion dans influxdb
add_measures(trame, time_measure) add_measures(trame)
# ajout timestamp pour debugger # ajout timestamp pour debugger
trame["timestamp"] = int(time_measure) trame["timestamp"] = int(time_measure)
logging.debug(trame) logging.debug(trame)
trame = dict() # on repart sur une nouvelle trame trame = dict() # on repart sur une nouvelle trame
delaycounter=0
except Exception as e: except Exception as e:
logging.error("Exception : %s" % e, exc_info=True) logging.error("Exception : %s" % e, exc_info=True)
logging.error("%s %s" % (key, val)) logging.error("%s" % (line))
line = ser.readline()
if __name__ == '__main__': if __name__ == '__main__':