sensord/sensord/RRD.py
2019-10-07 18:58:28 +02:00

158 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']) + ' &deg;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>')