157 lines
6.9 KiB
Python
157 lines
6.9 KiB
Python
""" Module to Store Sensor Data into an RRD Database and update Corresponding Graphs """
|
|
|
|
import _thread, time
|
|
import rrdtool
|
|
|
|
class RRDDB(object):
|
|
""" Class to Represent the RRD Data of a Single Multi-Value Sensor """
|
|
|
|
def __init__(self, rrdfile, sid, imgpath=None, sensor_type="thp"):
|
|
""" Initialize Class
|
|
|
|
Arguments:
|
|
rrdfile: Path to the RRD Database File (mandatory)
|
|
sid: Sensor ID which this RRD Database is for (mandatory)
|
|
imgpath: Path to Store Images, if set Images will be updated on call of notify()
|
|
sensor_type: Any combination of the following Letters:
|
|
t - Temperature
|
|
h - Humidity
|
|
p - Pressure
|
|
Default is 'thp' for a sensor sending all 3 values
|
|
"""
|
|
|
|
self.__rrdfile = rrdfile
|
|
self.__sid = sid
|
|
self.__imgpath = imgpath
|
|
self.__t = bool('t' in sensor_type)
|
|
self.__h = bool('h' in sensor_type)
|
|
self.__p = bool('p' in sensor_type)
|
|
|
|
def create(self, interval):
|
|
""" Create a new RRD Database According to the Specified Sensor Types
|
|
|
|
Arguments:
|
|
interval: Seconds between Sensor Updates
|
|
"""
|
|
|
|
datasrc = []
|
|
datasrc.append('DS:v:GAUGE:' + str(interval*2) + ':U:U')
|
|
datasrc.append('DS:r:GAUGE:' + str(interval*2) + ':U:U')
|
|
if self.__t:
|
|
datasrc.append('DS:t:GAUGE:' + str(interval*2) + ':U:U')
|
|
if self.__h:
|
|
datasrc.append('DS:h:GAUGE:' + str(interval*2) + ':U:U')
|
|
if self.__p:
|
|
datasrc.append('DS:p:GAUGE:' + str(interval*2) + ':U:U')
|
|
|
|
rrdtool.create(self.__rrdfile,
|
|
'--step', str(interval),
|
|
datasrc,
|
|
'RRA:LAST:0.5:1:105120',
|
|
'RRA:AVERAGE:0.5:6:175680',
|
|
'RRA:MAX:0.5:6:175680',
|
|
'RRA:MIN:0.5:6:175680')
|
|
|
|
def notify(self, msg):
|
|
""" Notify Method Called from the Receiver Class, Inserts Values into the RRD Database
|
|
|
|
Arguments:
|
|
msg: Object Representation of the Sensor Data
|
|
"""
|
|
|
|
if msg['sid'] == self.__sid:
|
|
updstr = 'N:' + str(msg['v']) + ':' + str(msg['rssi'])
|
|
if self.__t:
|
|
updstr = updstr + ':' + str(msg['t'])
|
|
if self.__h:
|
|
updstr = updstr + ':' + str(msg['h'])
|
|
if self.__p:
|
|
updstr = updstr + ':' + str(msg['p'])
|
|
rrdtool.update(self.__rrdfile, updstr)
|
|
|
|
if self.__imgpath != None:
|
|
_thread.start_new_thread(self.update_web, ())
|
|
|
|
def _graph(self, value, start):
|
|
""" Draw a Graph for a Specified Sensor Value
|
|
|
|
Arguments:
|
|
value: Sensor Identifier (t, h, p, v or r)
|
|
start: When to Start the Graph (24h, 7d, 30d, 1y)
|
|
"""
|
|
|
|
types = {'t': ['Temperatur', '#FF0000', '°C'],
|
|
'h': ['Luftfeuchte', '#0000FF', '%%'],
|
|
'p': ['Luftdruck', '#00FF00', 'hPa'],
|
|
'v': ['Batterie', '#FFFF00', 'V'],
|
|
'r': ['RSSI', '#00FFFF', '']}
|
|
times = {'24h': ['Letzte 24h', 'LAST'],
|
|
'7d': ['Letzte 7 Tage', 'AVERAGE'],
|
|
'30d': ['Letzte 30 Tage', 'AVERAGE'],
|
|
'1y': ['Letzte 12 Monate', 'AVERAGE']}
|
|
|
|
rrdtool.graph(self.__imgpath + '/graph_' + value + '_' + start + '.png',
|
|
'-s', 'now-' + start, '-e', 'now',
|
|
'-t', times[start][0], '-z', '-A',
|
|
'DEF:val=' + self.__rrdfile + ':'+ value + ':' + times[start][1],
|
|
'VDEF:valmax=val,MAXIMUM',
|
|
'VDEF:valmin=val,MINIMUM',
|
|
'VDEF:valavg=val,AVERAGE',
|
|
'LINE2:val'+types[value][1]+':'+types[value][0],
|
|
'GPRINT:valavg:Avg %2.2lf '+types[value][2],
|
|
'GPRINT:valmin:Min %2.2lf '+types[value][2],
|
|
'GPRINT:valmax:Max %2.2lf '+types[value][2])
|
|
|
|
def update_web(self):
|
|
""" Renew RRD Graphs and HTML for the Sensor Values """
|
|
|
|
if self.__t:
|
|
self._graph('t', '24h')
|
|
self._graph('t', '7d')
|
|
self._graph('t', '30d')
|
|
self._graph('t', '1y')
|
|
if self.__h:
|
|
self._graph('h', '24h')
|
|
self._graph('h', '7d')
|
|
self._graph('h', '30d')
|
|
self._graph('h', '1y')
|
|
if self.__p:
|
|
self._graph('p', '24h')
|
|
self._graph('p', '7d')
|
|
self._graph('p', '30d')
|
|
self._graph('p', '1y')
|
|
|
|
last = rrdtool.lastupdate(self.__rrdfile)
|
|
|
|
with open(self.__imgpath + '/index.html', 'w') as html:
|
|
html.write('<html>\n<head>\n')
|
|
html.write('<link rel="stylesheet" type="text/css" href="theme.css">\n')
|
|
html.write('<meta http-equiv="refresh" content="300">')
|
|
html.write('<title>Sensorlog RBS 3.OG</title>\n</head>')
|
|
html.write('<body>\n<div id="header">\n')
|
|
html.write('<h1>Sensorlog RBS 3.OG</h1></div>\n<div id="content">')
|
|
if self.__t:
|
|
html.write('<div><h3>Temperatur</h3>')
|
|
html.write('<p id="current">Aktuell ' + str(last['ds']['t']) + ' °C</p>')
|
|
html.write('<img src="./graph_t_24h.png" alt="Daily Graph" />')
|
|
html.write('<img src="./graph_t_7d.png" alt="Weekly Graph" />')
|
|
html.write('<img src="./graph_t_30d.png" alt="Monthly Graph" />')
|
|
html.write('<img src="./graph_t_1y.png" alt="Yearly Graph" /></div>\n')
|
|
if self.__h:
|
|
html.write('<div><h3>Luftfeuchte</h3>')
|
|
html.write('<p id="current">Aktuell ' + str(last['ds']['h']) + ' %</p>')
|
|
html.write('<img src="./graph_h_24h.png" alt="Daily Graph" />')
|
|
html.write('<img src="./graph_h_7d.png" alt="Weekly Graph" />')
|
|
html.write('<img src="./graph_h_30d.png" alt="Monthly Graph" />')
|
|
html.write('<img src="./graph_h_1y.png" alt="Yearly Graph" /></div>\n')
|
|
if self.__p:
|
|
html.write('<div><h3>Luftdruck</h3>')
|
|
html.write('<p id="current">Aktuell ' + str(last['ds']['p']) + ' hPa</p>')
|
|
html.write('<img src="./graph_p_24h.png" alt="Daily Graph" />')
|
|
html.write('<img src="./graph_p_7d.png" alt="Weekly Graph" />')
|
|
html.write('<img src="./graph_p_30d.png" alt="Monthly Graph" />')
|
|
html.write('<img src="./graph_p_1y.png" alt="Yearly Graph" /></div>\n')
|
|
if float(last['ds']['v']) < 2.5:
|
|
html.write('<p class="warning">Batterien sind fast leer, bitte Wechseln!</p>')
|
|
html.write('<p>Letztes Update: '+time.strftime('%d.%m.%Y %H:%M:%S')+' | Batteriespannung: '+str(last['ds']['v'])+'V </p>')
|
|
html.write('</div></body></html>')
|