teleinfo-linky-with-raspberry/teleinfo.py

163 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "Sébastien Reuiller"
# __licence__ = "Apache License 2.0"
# Python 3, prérequis : pip install pySerial influxdb
import logging
import time
from datetime import datetime
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 = [
'EAST',
'EASF',
'EASD',
'EAIT',
'ERQ',
'IRMS',
'URMS',
'PREF',
'PCOUP',
'SINST',
'SMAX',
'CCA',
'UMOY',
]
# création du logguer
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(host='192.168.0.10', port=8086, username='teleinfo', password='CLnXahFiUHjVF8xIjsLS')
DB_NAME = "teleinfo"
connected = False
while not connected:
try:
logging.info("Database %s exists?" % DB_NAME)
if not {'name': DB_NAME} in client.get_list_database():
logging.info("Database %s creation.." % DB_NAME)
client.create_database(DB_NAME)
logging.info("Database %s created!" % DB_NAME)
client.switch_database(DB_NAME)
logging.info("Connected to %s!" % DB_NAME)
except requests.exceptions.ConnectionError:
logging.info('InfluxDB is not reachable. Waiting 5 seconds to retry.')
time.sleep(5)
else:
connected = True
def add_measures(measures):
points = []
for measure, values in measures.items():
point = {
"measurement": measure,
"tags": {
# identification de la sonde et du compteur
"host": "raspberry",
"region": "linky"
},
"time": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
"fields": values
}
points.append(point)
client.write_points(points)
def verif_checksum(data, checksum):
data_unicode = 0
for caractere in data:
data_unicode += ord(caractere)
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/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
line = ser.readline()
while b'\x02' not in line: # recherche du caractère de début de trame
line = ser.readline()
delaycounter = DELAY_MESURE
while True:
while delaycounter < DELAY_MESURE:
ser.read_until(b'\x03')
delaycounter+=1
line = ser.readline()
logging.debug(line)
try:
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
time_measure = time.time()
# insertion dans influxdb
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" % (line))
if __name__ == '__main__':
if connected:
main()