summaryrefslogtreecommitdiff
path: root/envmon
diff options
context:
space:
mode:
authorChristian Franke <nobody@nowhere.ws>2013-06-20 19:25:15 +0200
committerChristian Franke <nobody@nowhere.ws>2013-06-20 19:25:31 +0200
commit9191312aac429e745388029ba57a29f015378541 (patch)
tree43fbbf740d0e2a0a231accb2db8e5f9df3af6c8c /envmon
parent0ab0cbbda438fd30f24273f1db99d76746108a75 (diff)
Add envmon code
Diffstat (limited to 'envmon')
-rw-r--r--envmon/colorgen.py51
-rwxr-xr-xenvmon/draw-graph.py49
-rw-r--r--envmon/onewire-ow.py46
-rw-r--r--envmon/onewire-sensors.txt8
-rw-r--r--envmon/onewire-sysfs.py59
l---------envmon/onewire.py1
-rwxr-xr-xenvmon/onewire2rrd.py18
-rwxr-xr-xenvmon/outdoor2rrd.py21
-rw-r--r--envmon/rrdlog.py13
-rw-r--r--envmon/weather.py24
10 files changed, 290 insertions, 0 deletions
diff --git a/envmon/colorgen.py b/envmon/colorgen.py
new file mode 100644
index 0000000..0d0e973
--- /dev/null
+++ b/envmon/colorgen.py
@@ -0,0 +1,51 @@
+import colorsys
+import random
+
+class ColorGen():
+ def __init__(self, seed=0):
+ self.random = random.Random()
+ self.random.seed(seed)
+ self.color_list = [(1.0,1.0,1.0)]
+
+ def rate(self, color):
+ distances = []
+ for other_color in self.color_list:
+ distances.append(self.distance(color, other_color))
+ return self.get_score(distances)
+
+ def get_score(self, distances):
+ return min(distances)
+
+ def distance(self, a, b):
+ return ((a[0]-b[0])**2 + (a[1]-b[1])**2 + (a[2]-b[2])**2) ** 0.5
+
+ def mutate(self, candidate):
+ rv = [0,0,0]
+ for i in range(0,2):
+ rv[i] = candidate[i] + self.random.uniform(-0.1,0.1)
+ if rv[i] < 0:
+ rv[i] = 0.0
+ if rv[i] > 1:
+ rv[i] = 1.0
+ return tuple(rv)
+
+ def get_next(self):
+ candidates = []
+ for i in range(0, 50): # Create 50 candidates
+ candidates.append((
+ self.random.uniform(0,1),
+ self.random.uniform(0,1),
+ self.random.uniform(0,1)
+ ))
+
+ for r in range(0, 40): # Do 40 Rounds
+ for c in list(candidates):
+ for i in range(0,5): # Create 5 decendants of each candidate
+ candidates.append(self.mutate(c))
+ # Keep the best ten candidates
+ candidates.sort(key=lambda candidate:-self.rate(candidate))
+ candidates = candidates[:10]
+
+ # And the winner is:
+ self.color_list.append(candidates[0])
+ return candidates[0]
diff --git a/envmon/draw-graph.py b/envmon/draw-graph.py
new file mode 100755
index 0000000..4e9d637
--- /dev/null
+++ b/envmon/draw-graph.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+import colorgen
+import rrdtool
+import time
+import onewire
+import sys
+
+dir_prefix = '/home/nihilus/envmon/'
+
+datasets = sorted([ (sensor, 'onewire-temp-%s.rrd' % sensor) for sensor in onewire.sensors() ], key = lambda x:x[0])
+datasets += [ ('Outside', 'env-outside.rrd') ]
+
+def graph(name, duration):
+ print >>sys.stderr, "Graphing %s" % name
+ end_struct = time.localtime(time.time() - 450)
+ end = time.mktime(end_struct) - end_struct.tm_sec - 60 * (end_struct.tm_min % 5)
+
+ prefix = [
+ name,
+ '-E',
+ '--end', '%d' % end,
+ '--start', 'end-%ds' % duration,
+ '--width', '800',
+ '--height', '300'
+ ]
+
+ body = []
+ cg = colorgen.ColorGen()
+ for dataset_name, dataset_rrd in datasets:
+ body.append('DEF:temperature-%(name)s=%(filename)s:temperature:AVERAGE:start=end-%(duration)d' % {
+ 'name': dataset_name,
+ 'filename': dir_prefix + dataset_rrd,
+ 'duration': duration + 3600
+ })
+ color = cg.get_next()
+ color = (int(color[0] * 255), int(color[1] * 255), int(color[2] * 255))
+ body.append('LINE2:temperature-%(name)s#%(r)02x%(g)02x%(b)02x:%(name)s' % {
+ 'name': dataset_name,
+ 'r': color[0],
+ 'g': color[1],
+ 'b': color[2]
+ })
+ rrdtool.graph(prefix + body)
+ print >>sys.stderr, "Done with %s" % name
+
+graph('env-day.png', 86400)
+graph('env-week.png', 604800)
+graph('env-month.png', 2592000)
+graph('env-year.png', 31557600)
diff --git a/envmon/onewire-ow.py b/envmon/onewire-ow.py
new file mode 100644
index 0000000..6e3420b
--- /dev/null
+++ b/envmon/onewire-ow.py
@@ -0,0 +1,46 @@
+"""
+Onewire interface using owserver
+"""
+
+import os
+import ow
+
+class OnewireException(Exception):
+ pass
+
+ow.init('127.0.0.1:4304')
+
+_sensors = {}
+with open(os.path.join(os.path.realpath(os.path.dirname(__file__)), 'onewire-sensors.txt'), 'r') as sensors_file:
+ for line in sensors_file:
+ line = line.strip()
+ address, name = line.split(' ', 1)
+ _sensors[name] = address
+
+def sensors():
+ return list(_sensors.keys())
+
+class SensorFacade(object):
+ def __init__(self, sensor, name):
+ self._sensor = sensor
+ self._name = name
+
+ def __getattr__(self, name):
+ return getattr(self._sensor, name)
+
+ @property
+ def temperature(self):
+ for i in range(1,3):
+ rv = float(self._sensor.temperature)
+ if rv > 65:
+ continue
+ if rv < -25:
+ continue
+ return rv
+ raise OnewireException("Bus error for %s" % self._name)
+
+def sensor(name):
+ if name not in _sensors:
+ raise OnewireException('Don\'t know about sensor %s' % name)
+
+ return SensorFacade(ow.Sensor('/%s' % _sensors[name]), name)
diff --git a/envmon/onewire-sensors.txt b/envmon/onewire-sensors.txt
new file mode 100644
index 0000000..86771e3
--- /dev/null
+++ b/envmon/onewire-sensors.txt
@@ -0,0 +1,8 @@
+10.0008020BB209 hacklab
+10.0008020B772C rack
+10.0008020BB966 office
+10.0008020BAC81 clockwork
+10.0008020BA18F workshop
+28.0000031E4328 lounge
+28.0000031E74F8 stage
+28.0000031E3C90 test
diff --git a/envmon/onewire-sysfs.py b/envmon/onewire-sysfs.py
new file mode 100644
index 0000000..20f3fde
--- /dev/null
+++ b/envmon/onewire-sysfs.py
@@ -0,0 +1,59 @@
+"""
+Onewire interface using w1 sysfs
+"""
+
+import os
+import sys
+import time
+
+class OnewireException(Exception):
+ pass
+
+_sensors = {}
+with open(os.path.join(os.path.realpath(os.path.dirname(__file__)), 'onewire-sensors.txt'), 'r') as sensors_file:
+ for line in sensors_file:
+ line = line.strip()
+ address, name = line.split(' ', 1)
+ _sensors[name] = address
+
+def sensors():
+ return list(_sensors.keys())
+
+class SensorFacade(object):
+ def __init__(self, addr, name):
+ self._addr = addr
+ self._name = name
+
+ def get_temperature(self):
+ addr = self._addr.replace('.', '-').lower()
+ path = '/sys/devices/w1_bus_master1/{0}/w1_slave'.format(addr)
+
+ with open(path, 'r') as w1_file:
+ w1_data = w1_file.read()
+
+ match = re.search(r'\st=(\d+)', w1_data)
+ temp = float(match.group(1)) / 1000
+ return temp
+
+ @property
+ def temperature(self):
+ for i in range(1,3):
+ try:
+ rv = self.get_temperature()
+ except Exception:
+ sys.excepthook(*sys.exc_info())
+ time.sleep(0.5)
+ continue
+
+ if rv > 65:
+ continue
+ if rv < -25:
+ continue
+ return rv
+ raise OnewireException("Bus error for %s" % self._name)
+
+def sensor(name):
+ if name not in _sensors:
+ raise OnewireException('Don\'t know about sensor %s' % name)
+
+ return SensorFacade('%s' % _sensors[name], name)
diff --git a/envmon/onewire.py b/envmon/onewire.py
new file mode 120000
index 0000000..32460f9
--- /dev/null
+++ b/envmon/onewire.py
@@ -0,0 +1 @@
+onewire-sysfs.py \ No newline at end of file
diff --git a/envmon/onewire2rrd.py b/envmon/onewire2rrd.py
new file mode 100755
index 0000000..d3d2927
--- /dev/null
+++ b/envmon/onewire2rrd.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+import time
+import sys
+
+import onewire
+import rrdlog
+
+while True:
+ for sensor in onewire.sensors():
+ try:
+ rrdlog.TempLog('onewire-temp-%s.rrd' % sensor).update(onewire.sensor(sensor).temperature)
+ except Exception:
+ print >>sys.stderr, "On %s: Could not retrieve temperature for '%s':" % (
+ time.strftime('%a, %d %b %Y %T %z'), sensor)
+ sys.excepthook(*sys.exc_info())
+ print >>sys.stderr, '========================================'
+ time.sleep(300-len(onewire.sensors()))
diff --git a/envmon/outdoor2rrd.py b/envmon/outdoor2rrd.py
new file mode 100755
index 0000000..1c4ca24
--- /dev/null
+++ b/envmon/outdoor2rrd.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+import time
+import sys
+
+import rrdlog
+import weather
+
+location = '20065491' # Leipzig
+
+while True:
+ try:
+ data = weather.weather(location)
+ rrdlog.TempLog('env-outside.rrd').update(data.temperature)
+ except Exception:
+ sys.stderr.write('Exception occured at %s:\n' % time.strftime('%a, %d %b %Y %T %z'))
+ sys.excepthook(*sys.exc_info())
+ sys.stderr.write('========================================\n')
+ time.sleep(60)
+ else:
+ time.sleep(300)
diff --git a/envmon/rrdlog.py b/envmon/rrdlog.py
new file mode 100644
index 0000000..4dd4708
--- /dev/null
+++ b/envmon/rrdlog.py
@@ -0,0 +1,13 @@
+import rrdtool
+import os
+
+class TempLog(object):
+ def __init__(self, name):
+ self.name = name
+ if not os.path.exists(self.name):
+ rrdtool.create(self.name, 'DS:temperature:GAUGE:600:U:U',
+ 'RRA:AVERAGE:0.5:1:17280', # Keep 5min snapshots for the last two months
+ 'RRA:AVERAGE:0.5:12:87660', # Keep 1h averages for 10 years (as if...)
+ )
+ def update(self, temperature):
+ rrdtool.update(self.name, 'N:%f' % temperature)
diff --git a/envmon/weather.py b/envmon/weather.py
new file mode 100644
index 0000000..c5396c6
--- /dev/null
+++ b/envmon/weather.py
@@ -0,0 +1,24 @@
+import contextlib
+import urllib2
+import xml.etree.ElementTree
+
+class WeatherData(object):
+ def __init__(self):
+ pass
+
+def weather(woeid):
+ """
+ Takes a woeid as argument (see yahoo api docs) and returns a dictionary
+ containing weather data.
+ """
+
+ with contextlib.closing(urllib2.urlopen('http://weather.yahooapis.com/forecastrss?w=%s&u=c' % woeid, timeout=2.0)) as request:
+ tree = xml.etree.ElementTree.fromstring(request.read())
+
+ yweather = 'http://xml.weather.yahoo.com/ns/rss/1.0'
+ results = WeatherData()
+
+ results.temperature = float(tree.find('.//{%s}condition' % yweather).get('temp'))
+ results.humidity = float(tree.find('.//{%s}atmosphere' % yweather).get('humidity'))
+
+ return results