Archives de tags | MySQL

Lire les données d’une chaudière Hargassner par telnet en python 2.7

Les chaudières Hargassner équipées du touchtronic (de séries depuis 2015, en options avant)  émettent leurs données sur un port telnet, celles avec l’ancienne interface émettent sur un port série.

Je vous présente ici mon projet qui permet d’écouter une chaudière Hargassner  sur son port telnet.

un script python tourne en permanence sur mon raspberry , il récupère les données et les écrits dans une base MariaDB sur mon NAS Synology , ensuite j’affiche les courbes avec un site web en php + Highchart

le projet python se trouve sur github https://github.com/Jahislove/hargassner-python

la partie web ici https://github.com/Jahislove/Hargassner

 

harg3 harg2 harg1

Stocker la température dans une base MySQL en Python sur le Raspberry – Partie 3

La série d’articles :

  • Partie 1 : Installation capteur DS18B20 et lecture en ligne de commande
  • Partie 2 : Lecture des valeurs avec un script Python basique
  • Partie 3 : Lecture des valeurs avec un script Python avancé et stockage dans une base MySQL
  • Partie 4 : Visualisation des données sur une page web

Le matériel nécessaire :

  • 1 raspberry pi fonctionnel avec Raspbian installé
  • 1 base de données MySQL ou MariaDB en local ou sur un autre serveur

Dans la partie 1 de cet article nous avons vu comment lire un capteur de température avec un raspberry.

L’intérêt de tout cela c’est surtout de pouvoir stocker les températures sur une longue durée et de pouvoir faire des jolis graphiques

Nous allons donc stocker tout ça dans une base MySQL. Je ne vais pas décrire ici l’installation et la configuration de MySQL , pour cela google est votre ami.

Pré-requis :

1 raspberry avec raspbian et python 2.7 (ne fonctionne pas avec python 3)

1 base de données déja fonctionnelle ( MySQL ou MariaDB, les 2 sont completement compatibles)

et bien sur avoir suivi la partie 1

python-logole script qui va faire cela est en Python 2.7 , la base de données est une MySQL/MariaDB stockée sur un NAS Synology.

mais vous pouvez aussi installer MySQL sur le raspberry en local

Je parles de MariaDB car initialement j’avais développé le script pour MySQL mais suite au rachat de MySQL par Oracle , un fork MariaDB entièrement compatible libre a été crée. et sur Mon NAS Synology la migration a été automatique sans me demander mon avis. mais au final je n’ai eu aucune modif a faire sur le script .

 Ne vous laissez pas impressionner par la taille du script , en réalité la partie essentielle fait 1 page , tout le reste c’est du control d’erreur 

ces controles permettent les choses suivantes:

  •  éviter au script de planter si jamais la base est inaccessible 
  •  stocker les données dans une base SQlite en local durant l’indisponibilité de  la base MySQL
  •  restorer les mesures depuis SQlite vers MySQL dés que celle ci est de nouveau dispo.

Nous avons besoin d’ajouter 2 modules :

SQlite3 : installation SQlite3  qui va servir de backup

python-mysqldb : permet à python de dialoguer avec une base MySQL,MariaDB,SQLite

sudo apt-get install python-mysqldb
sudo apt-get install sqlite3

Si vous ne voulez pas vous embeter avec la base SQlite de secours , je vais poster en bas de cet article un script beaucoup plus simple , ne comportant que l’essentiel

Vous pouvez retrouver ce script sur mon github au cas ou vous auriez des problèmes de copier/coller :

https://github.com/Jahislove/Thermo-pi/blob/master/thermo.py

Le script python est commenté pour vous aider a le comprendre

#!/usr/bin/python
# -*- coding: utf-8 -*-

#=========================================================================
#              thermo.py
#-------------------------------------------------------------------------
# by JahisLove - 2014, june
# version 0.1 2014-06-16
#-------------------------------------------------------------------------
# ce script lit les temperatures donnees par 3 sondes DS18B20 reliees
# au raspberry pi et les stock dans une base MySQL
#
#
# tested with python 2.7 on Raspberry pi (wheezy) and MariaDB 5.5.34 on NAS Synology DS411J (DSM 5)
#
#-------------------------------------------------------------------------
#
# la base de données doit avoir cette structure:
#CREATE TABLE `PiTemp` (
#  `id` int(11) NOT NULL AUTO_INCREMENT,
#  `date` datetime NOT NULL,
#  `sonde1` decimal(3,1) NOT NULL,
#  `sonde2` decimal(3,1) NOT NULL,
#  `sonde3` decimal(3,1) NOT NULL,
#  PRIMARY KEY (`id`)
#) ENGINE=InnoDB  DEFAULT CHARSET=latin1  ;

#===================================================================

#----------------------------------------------------------#
#             package importation                          #
#----------------------------------------------------------#
import os
import time
import MySQLdb   # MySQLdb must be installed by yourself
import sqlite3

#-----------------------------------------------------------------#
#  constants : use your own values / utilisez vos propres valeurs #
#-----------------------------------------------------------------#
PATH_THERM = "/home/pi/thermo/" #path to this script
DB_SERVER ='xxx.xxx.xxx.xxx'  # MySQL : IP server (localhost if mySQL is on the same machine)
DB_USER='xxxxxxxx'     # MySQL : user
DB_PWD='xxxxxxxx'            # MySQL : password
DB_BASE='xxxxxxxxx'     # MySQL : database name

# vous pouvez ajouter ou retirer des sondes en modifiant les 5 lignes ci dessous
# ainsi que la derniere ligne de ce script : querydb(....
sonde1 = "/sys/bus/w1/devices/w1_bus_master1/28-000005f2424d/w1_slave"
sonde2 = "/sys/bus/w1/devices/w1_bus_master1/28-000005f2764e/w1_slave"
sonde3 = "/sys/bus/w1/devices/w1_bus_master1/28-000005f396a0/w1_slave"
sondes = [sonde1, sonde2, sonde3]
sonde_value = [0, 0, 0]

#----------------------------------------------------------#
#             Variables                                    #
#----------------------------------------------------------#

backup_row = 0
backup_mode = 0

if os.path.isfile(PATH_THERM + 'therm_bck.sqlite3'): # if sqlite exist then resume backup mode
    backup_mode = 1

#----------------------------------------------------------#
#     definition : database query with error handling      #
#----------------------------------------------------------#

def query_db(sql):
    global backup_mode
    global backup_row
    try:
        db = MySQLdb.connect(DB_SERVER, DB_USER, DB_PWD, DB_BASE)
        cursor = db.cursor()
        #---------------------------------------------------------------#
        #     Normal MySQL database INSERT                              #
        #     Fonctionnement normal, insertion dans MySQL               #
        #---------------------------------------------------------------#
        if backup_mode == 0:
            cursor.execute(sql)
            db.commit()
            db.close()
        #---------------------------------------------------------------#
        # RESTORE : when MySQL is available again : restore from SQlite #
        # restauration depuis SQlite vers MySQL apres une indispo de MySQL#
        #---------------------------------------------------------------#
        else:
            logfile = open(PATH_THERM + "thermo.log", "a")
            log = time.strftime('%Y-%m-%d %H:%M:%S') + " INFO : MySQL is OK now : Restore mode started\n"
            logfile.write(log)

            db_bck = sqlite3.connect(PATH_THERM + 'therm_bck.sqlite3')
            db_bck.text_factory = str #tell sqlite to work with str instead of unicode
            cursor_bck = db_bck.cursor()

            cursor_bck.execute("""SELECT 'DEFAULT' as id, date, sonde1, sonde2, sonde3 FROM PiTemp ORDER BY date ASC """)
            result_pitemp = cursor_bck.fetchall ()

            for row in result_pitemp:
                cursor.execute("""INSERT INTO PiTemp VALUES {0}""".format(row))

            db_bck.close()
            log = time.strftime('%Y-%m-%d %H:%M:%S') + " INFO : " + str(backup_row) + " rows restored to MySQL\n"
            logfile.write(log)

            backup_row = 0
            backup_mode = 0
            os.remove(PATH_THERM + 'therm_bck.sqlite3')
            log = time.strftime('%Y-%m-%d %H:%M:%S') + " INFO : restore done, sqlite3 file deleted, returning to normal mode\n"
            logfile.write(log)

            cursor.execute(sql)
            db.commit()
            db.close()
            logfile.close

        #---------------------------------------------------------------#
        #     BACKUP : when MySQL is down => local SQlite INSERT        #
        #  si MySQL est KO , on créé une base SQlite temporaire         #
        #---------------------------------------------------------------#
    except MySQLdb.Error:
        db_bck = sqlite3.connect(PATH_THERM + 'therm_bck.sqlite3')
        cursor_bck = db_bck.cursor()

        if backup_mode == 0: #create table on first run
            logfile = open(PATH_THERM + "thermo.log", "a")
            log = time.strftime('%Y-%m-%d %H:%M:%S') + " WARN : MySQL is down : Backup mode started\n"
            logfile.write(log)

            create_pitemp = """CREATE TABLE IF NOT EXISTS PiTemp (`date` datetime NOT NULL,
              sonde1 decimal(3,1) NOT NULL, sonde2 decimal(3,1) NOT NULL,sonde3 decimal(3,1) NOT NULL
            ) ;"""

            cursor_bck.execute(create_pitemp)

            log = time.strftime('%Y-%m-%d %H:%M:%S') + " WARN : Sqlite created\n"
            logfile.write(log)
            logfile.close

        backup_mode = 1
        cursor_bck.execute(sql)
        backup_row += 1
        db_bck.commit()
        db_bck.close()

#----------------------------------------------------------#

def read_file(sonde):
        """ fonction de lecture d'une sonde """
	try:
		f = open(sonde, 'r')
		lines = f.readlines()
		f.close()
	except:
		time.sleep(60)
		try:
			f = open(sonde, 'r')
			lines = f.readlines()
			f.close()
		except:
			logfile = open(PATH_THERM + "thermo.log", "a")
			log = time.strftime('%Y-%m-%d %H:%M:%S') + " WARN : Sonde not found\n"
			logfile.write(log)
			logfile.close
			exit

	finally:
		return lines

#----------------------------------------------------------#
#             code principal                               #
#----------------------------------------------------------#

# initialize Raspberry GPIO and DS18B20
os.system('sudo /sbin/modprobe w1-gpio')
os.system('sudo /sbin/modprobe w1-therm')
time.sleep(2)

datebuff = time.strftime('%Y-%m-%d %H:%M:%S') #formating date for mySQL

for (i, sonde) in enumerate(sondes):
	lines = read_file(sonde)
	while lines[0].strip()[-3:] != 'YES': # read 3 last char from line 0 and retry if not yes
		time.sleep(0.2)
		lines = read_file(sonde)

	temp_raw = lines[1].split("=")[1]     # when YES then read temp (after =) in second line
	sonde_value[i] = round(int(temp_raw) / 1000.0, 1)

#ecriture dans la base
query_db("""INSERT INTO PiTemp (date, sonde1, sonde2, sonde3) VALUES ('%s','%s','%s','%s')
         """ % (datebuff, sonde_value[0], sonde_value[1], sonde_value[2]))

 

Voila , il ne vous reste plus qu’a lancer ce script a intervalle régulier avec la crontab

Ci dessous , je vous met le meme script simplifié à l’extreme , il se contente de l’essentiel , il est plus facile a comprendre mais il n’y a aucun controle d’erreur

#!/usr/bin/python
# -*- coding: utf-8 -*-

#================================================================
#              thermo.py
#----------------------------------------------------------------
# by JahisLove - 2014, june
# version 0.1 2014-06-16
#
# la base de données doit avoir cette structure:
#CREATE TABLE `PiTemp` (
#  `id` int(11) NOT NULL AUTO_INCREMENT,
#  `date` datetime NOT NULL,
#  `sonde1` decimal(3,1) NOT NULL,
#  `sonde2` decimal(3,1) NOT NULL,
#  `sonde3` decimal(3,1) NOT NULL,
#  PRIMARY KEY (`id`)
#) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=28 ;

#================================================================

#----------------------------------------------------------#
#             package importation                          #
#----------------------------------------------------------#
import os
import time
import MySQLdb   # MySQLdb must be installed by yourself

#-----------------------------------------------------------------#
#  constants : use your own values / utilisez vos propres valeurs #
#-----------------------------------------------------------------#
PATH_THERM = "/home/pi/thermo/" #path to this script
DB_SERVER ='xxx.xxx.xxx.xxx'  # MySQL : IP server (localhost if mySQL is on the same machine)
DB_USER='xxxxxxxx'     # MySQL : user
DB_PWD='xxxxxxxx'            # MySQL : password
DB_BASE='xxxxxxxxx'     # MySQL : database name

# vous pouvez ajouter ou retirer des sondes en modifiant les 5 lignes ci dessous
# ainsi que la derniere ligne de ce script : querydb(....
sonde1 = "/sys/bus/w1/devices/w1_bus_master1/28-000005f2424d/w1_slave"
sonde2 = "/sys/bus/w1/devices/w1_bus_master1/28-000005f2764e/w1_slave"
sonde3 = "/sys/bus/w1/devices/w1_bus_master1/28-000005f396a0/w1_slave"
sondes = [sonde1, sonde2, sonde3]
sonde_value = [0, 0, 0]

#----------------------------------------------------------#
#     definition : database query                          #
#----------------------------------------------------------#

def query_db(sql):
    try:
        db = MySQLdb.connect(DB_SERVER, DB_USER, DB_PWD, DB_BASE) #creation du connecteur de la base
        cursor = db.cursor() # creation du curseur
        if backup_mode == 0:
            cursor.execute(sql) #execution de la requete
            db.commit()
            db.close()
#----------------------------------------------------------#
# lit le fichier sonde ,
def read_file(sonde):
    try:
        f = open(sonde, 'r')
        lines = f.readlines()
        f.close()
    except:
        exit

#----------------------------------------------------------#
#             code                                         #
#----------------------------------------------------------#

# initialize Raspberry GPIO and DS18B20
# si vous avez installé ces modules au boot dans /etc/modules
# vous pouvez supprimer les 3 lignes ci dessous
os.system('sudo /sbin/modprobe w1-gpio')
os.system('sudo /sbin/modprobe w1-therm')
time.sleep(2)

datebuff = time.strftime('%Y-%m-%d %H:%M:%S') #formating date for mySQL

for (i, sonde) in enumerate(sondes):
	lines = read_file(sonde)
	while lines[0].strip()[-3:] != 'YES': # read 3 last char from line 0 and retry if not yes
		time.sleep(0.2)
		lines = read_file(sonde)

	temp_raw = lines[1].split("=")[1]     # when YES then read temp (after =) in second line
	sonde_value[i] = round(int(temp_raw) / 1000.0, 1)

query_db("""INSERT INTO PiTemp (date, sonde1, sonde2, sonde3) VALUES ('%s','%s','%s','%s')
         """ % (datebuff, sonde_value[0], sonde_value[1], sonde_value[2])) # on INSERT dans la base