#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
| Copyright finizi 2014 - Créé le 25/03/2014
| www.DomoEnergyTICs.com
|
| Code sous licence GNU GPL :
| 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 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 .
"""
"""
Script python tournant sur le Raspberry Pi destiné à envoyer régulièrement
le flux de données de la téléinformation.
Fait aussi office de server web pour afficher sur le réseau local les
informations de téléinformation.
"""
import threading
import time
import serial
import string
import json
import urllib2
import bottle
######################################################################
class TeleInfo(object):
"""
Informations en cours issues du modem USBTIC de Téléinformation
Puissance apparente et intensité sont moyennées sur 2 intervalles possibles:
- Interval 'Normal': cas par exemple d'une interrogation locale toutes les 10 secondes
- Interval long: utilisé pour le data loggin
Le compteur EDF est un "Compteur Jaune", c'est à dire qu'il gère 4 compteurs
selon l'heure du jour et le jour dans l'année
"""
def __init__(self):
self._indexA = 0 # index du compteur A
self._indexB = 0 # index du compteur B
self._indexC = 0 # index du compteur C
self._indexD = 0 # index du compteur D
self._pa = -1 # puissance apparente
self._sumPa = -1 # somme des puissances apparentes durant l'intervalle
self._sumPaL = -1 # somme des puissances apparentes durant l'intervalle long
self._nbPa = 0 # nombre de puissances apparentes lues durant l'intervalle
self._nbPaL = 0 # nombre de puissances apparentes lues durant l'intervalle long
self._ts = "" # heure et minute fournie par la téléinfo (timestamp)
pass
def _get_indexA(self):
return(self._indexA)
def _set_indexA(self, newIndexA):
self._indexA = newIndexA
indexA = property( fget = _get_indexA,
fset = _set_indexA,
doc = "index A, en kW/h, affiché par le compteur." )
def _get_indexB(self):
return(self._indexB)
def _set_indexB(self, newIndexB):
self._indexB = newIndexB
indexB = property( fget = _get_indexB,
fset = _set_indexB,
doc = "index B, en kW/h, affiché par le compteur." )
def _get_indexC(self):
return(self._indexC)
def _set_indexC(self, newIndexC):
self._indexC = newIndexC
indexC = property( fget = _get_indexC,
fset = _set_indexC,
doc = "index C, en kW/h, affiché par le compteur." )
def _get_indexD(self):
return(self._indexD)
def _set_indexD(self, newIndexD):
self._indexD = newIndexD
indexD = property( fget = _get_indexD,
fset = _set_indexD,
doc = "index D, en kW/h, affiché par le compteur." )
def _get_pa(self):
dispPa = self._pa
if (self._nbPa > 0):
dispPa = self._sumPa / self._nbPa
self._sumPa = 0
self._nbPa = 0
return(dispPa)
def _set_pa(self, newPa):
self._pa = newPa
self._sumPa += newPa
self._nbPa += 1
self._sumPaL += newPa
self._nbPaL += 1
pa = property( fget = _get_pa,
fset = _set_pa,
doc = "Puissance apparente moyenne de l'intervalle 'normal', en 'VoltAmpères'" )
def _get_paL(self):
dispPaL = self._pa
if (self._nbPaL > 0):
dispPaL = self._sumPaL / self._nbPaL
self._sumPaL = 0
self._nbPaL = 0
return(dispPaL)
paL = property( fget = _get_paL,
doc = "Puissance apparente moyenne de l'interval long, en 'VoltAmpères'" )
def _get_ts(self):
return(self._ts)
def _set_ts(self, newTs):
self._ts = newTs
ts = property( fget = _get_ts,
fset = _set_ts,
doc = "Heure et minute fournie par la téléinfo (timestamp)." )
######################################################################
class readFromSerial(threading.Thread):
"""
Lecture du port USB sur lequel est branché la téléInformation.
La lecture se fait dans un thread différent.
"""
def __init__(self, ti):
self._OK = 0
try:
threading.Thread.__init__(self)
self.setDaemon(True)
self._ti = ti
print 'Initialize serial'
self._ser = serial.Serial( port='/dev/ttyUSB0',
baudrate=1200,
parity=serial.PARITY_EVEN,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.SEVENBITS,
timeout=1)
print 'Serial initialized'
self._OK = 1
except:
print 'Serial teleinfo error'
def __del__(self):
self._ser.close()
def run(self):
while True:
try:
tiLine = self._ser.readline()
if (string.find(tiLine, 'JAUNE ') == 0):
self._ti.ts = tiLine[6:11]
if (tiLine[24:29].isdigit() == True):
self._ti.pa = 10 * int(tiLine[24:29])
if (string.find(tiLine, 'ENERG ') == 0):
if (tiLine[6:12].isdigit() == True):
self._ti.indexA = int(tiLine[6:12])
if (tiLine[13:19].isdigit() == True):
self._ti.indexB = int(tiLine[13:19])
if (tiLine[20:26].isdigit() == True):
self._ti.indexC = int(tiLine[20:26])
if (tiLine[27:33].isdigit() == True):
self._ti.indexD = int(tiLine[27:33])
except:
print 'Serial no data'
time.sleep(60)
######################################################################
class sendToServer(threading.Thread):
"""
Envoie toutes les minutes au server les donnes de téléInformation.
Le travaille se fait dans un thread différent.
"""
def __init__(self, ti):
self._OK = 0
try:
threading.Thread.__init__(self)
self.setDaemon(True)
self._ti = ti
print 'Initialize server'
self._OK = 1
except:
print 'server KO'
def run(self):
# Il faut utiliser un proxy pour sortir sur Internet
proxy_info = { 'host' : 'prxy-xxxxxxx-xxxxxx.yy', 'port' : 9999 }
proxy_support = urllib2.ProxyHandler({"http" : "http://%(host)s:%(port)d" % proxy_info})
# On créé un opener utilisant ce handler:
opener = urllib2.build_opener(proxy_support)
# Puis on installe cet opener comme opener par défaut du module urllib2.
urllib2.install_opener(opener)
baseUrl = "http://Url.Du.Serveur.Internet/chargé/du/datalogging/"
while True:
try:
# prepare l'URL
if (ti.pa >= 0):
dataUrl = '{0}idxA={1}&idxB={2}&idxC={3}&idxD={4}&pa={5}&ts={6}'.format(baseUrl, ti.indexA, ti.indexB, ti.indexC, ti.indexD, ti.paL, ti.ts)
# envoi au server
urllib2.urlopen(dataUrl)
except:
print 'Sender error'
# dort une minute
time.sleep(60)
######################################################################
# Instancie un site web
webApp = bottle.Bottle()
@webApp.route('/favicon.ico', method='ANY')
def get_favicon():
return bottle.static_file('favicon.ico', root='static')
@webApp.route('/static/')
def staticPages(fileName):
return bottle.static_file(fileName, root='static')
@webApp.route('/teleinfo')
def edf():
return bottle.static_file('teleinfo.html', root='static')
@webApp.route('/teleinfo/json', method='ANY')
def edfjson():
dict = {'idxA':ti.indexA, 'idxB':ti.indexB, 'idxC':ti.indexC, 'idxD':ti.indexD, 'pa':ti.pa }
return dict
######################################################################
if __name__ == '__main__':
# Instancie la classe de gestion de la TéléInfo
ti = TeleInfo()
# Instancie & démarre les threads de lecture de la TéléInfo
tiReader = readFromSerial(ti)
tiReader.start()
# Instancie & démarre les threads d'envoie des données
tiSender = sendToServer(ti)
tiSender.start()
# Lance le site web
bottle.run(webApp, host='0.0.0.0', port=8080, server='cherrypy')