Mercurial > code > home > repos > homeauto
diff service/onewire/onewire.py @ 4:be855a111619
move a bunch of services into this tree, give them all web status pages
Ignore-this: a11e90f9d2cd9470565c743f54943c4b
author | drewp@bigasterisk.com |
---|---|
date | Mon, 08 Aug 2011 00:31:31 -0700 |
parents | |
children | 1444da5242d8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/onewire/onewire.py Mon Aug 08 00:31:31 2011 -0700 @@ -0,0 +1,194 @@ +#!/usr/bin/python +""" +normal accessing of the 'temperature' field on the sensors wasn't +working. I always got '85' (the power-on reset value). owfs verison is 2.7p2 + +http://sourceforge.net/mailarchive/forum.php?thread_name=fba87cb90612051724o705bfed0ub780325b915ed541%40mail.gmail.com&forum_name=owfs-developers + +Asking for simultaneous read seems to work, and I'm fine with doing that. + +the stock modules for onewire are bad; they will take all your CPU. to +turn them off, see: +http://tomasz.korwel.net/2006/07/02/owfs-instalation-on-ubuntu-606/#comment-12246 + +For the python 'ow' package, get +http://downloads.sourceforge.net/owfs/owfs-2.7p7.tar.gz?modtime=1222687523&big_mirror=0 +or similar. Install the libusb-dev and swig packages first for usb and +python support. + +2009-02-21 i'm now on ow.__version__ = '2.7p16-1.15' +./configure --disable-owtcl --disable-owperl --disable-owphp --disable-ha7 --disable-ownet --disable-ownetlib --disable-owserver --disable-parport + +2011-02-26 now on 2.8p6 + +how to run their server: +bang(pts/6):/my/dl/lib/owfs-2.8p6/module/owserver/src/c% sudo ./owserver -u -p 9999 --foreground --error_level 9 --error_print 2 + +owshell/src/c/owget -s 9999 /uncached/10.52790F020800/temperature /uncached/10.4F718D000800/temperature /uncached/10.9AA2BE000800/temperature + + +but the previous 2.7 version was getting 2/3 measurements, while 2.8 +was getting 1/3 measurements! + +""" +from __future__ import division +import time, logging, traceback, sys, cyclone.web, jsonlib, restkit +from twisted.internet.task import LoopingCall, deferLater +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.internet import reactor +import ow + +sys.path.append("/my/proj/homeauto/lib") +from cycloneerr import PrettyErrorHandler +from logsetup import log + +sys.path.append("/my/proj/room") +from carbondata import CarbonClient + +class TempReader(object): + def __init__(self): + self.expectedSensors = 3 + self.ow = None + self.initOnewire() + self.firstSensorLoop = True + + def initOnewire(self): + """open usb connection and configure ow lib. Run this again if + things get corrupt""" + ow.init('u') + # this might PRINT a 'Could not open the USB adapter.' message, but I + # don't know how to trap it + + @inlineCallbacks + def getCompleteTemps(self, maxTime=120): + ret = {} + tries = 0 + now = time.time() + giveUp = now + maxTime + + self.requestTemps() + sensors = set(self.allSensors()) + + while now < giveUp: + tries += 1 + ret.update(self.getTemps(sensors - set(ret.keys()))) + + if len(ret) >= self.expectedSensors: + log.info("after %s tries, temps=%s" % (tries, ret)) + break + + log.debug("..only have %s measurements; still trying for %d secs" % + (len(ret), giveUp - now)) + self.initOnewire() + self.requestTemps() + yield deferLater(reactor, .5, lambda: None) + now = time.time() + else: + log.info("giving up after %s secs, only got %s measurements" % + (maxTime, len(ret))) + returnValue(dict([(s.address,val) for s, val in ret.items()])) + + def allSensors(self): + return ow.Sensor('/').sensors() + + def requestTemps(self): + ow.owfs_put('/uncached/simultaneous/temperature', '1') + + def getTemps(self, sensors): + ret = {} + try: + for sens in sensors: + if self.firstSensorLoop: + log.debug("found sensor address %r, type=%r" % + (sens.address, sens.type)) + if sens.type != 'DS18S20': + continue + try: + t = sens.temperature.strip() + if t == '85': + log.debug( + " sensor %s says 85 (C), power-on reset value" % + sens.address) + continue + tFar = float(t) * 9/5 + 32 + log.debug(" %s reports temp %r F" % (sens.address, tFar)) + except ow.exUnknownSensor, e: + log.warn(e) + continue + ret[sens] = tFar + except KeyboardInterrupt: raise + except Exception, e: + traceback.print_exc() + self.firstSensorLoop = False + return ret + + +class Poller(object): + def __init__(self): + self.reader = TempReader() + self.lastPollTime = 0 + self.lastDoc = [] + self.carbon = CarbonClient(serverHost='bang') + + def getHttpTemps(self): + ret = {} + + for url, name in [("http://star:9014/", "ariroom"), + ("http://space:9080/", "frontDoor"), + ]: + for tries in range(3): + try: + res = restkit.Resource(url, timeout=5) + temp = jsonlib.read(res.get("temperature").body_string(), + use_float=True)['temp'] + log.debug("got http temp %s = %r", name, temp) + ret[name] = temp + break + except Exception, e: + log.warn(e) + return ret + + @inlineCallbacks + def sendTemps(self): + try: + temps = yield self.reader.getCompleteTemps(maxTime=30) + except Exception, e: + reactor.stop() + raise + temps.update(self.getHttpTemps()) + now = time.time() + rows = [] + for k, v in temps.items(): + row = 'system.house.temp.%s' % { + '104F718D00080038': 'downstairs' , + '109AA2BE000800C7': 'livingRoom', + '1052790F02080086' : 'bedroom', + '1014958D0008002B': 'unused1', # when you set this, fix expectedSensors count too + '10CB6CBE0008005E': 'bedroom-broken', + }.get(str(k), str(k)), float(v) + self.carbon.send(row[0], row[1], now) + rows.append(row) + + self.lastPollTime = now + self.lastDoc = rows + +class Index(PrettyErrorHandler, cyclone.web.RequestHandler): + def get(self): + + dt = time.time() - self.settings.poller.lastPollTime + if dt > 120 + 50: + raise ValueError("last poll %s sec ago" % dt) + + self.set_header("Content-Type", "text/plain") + self.write("onewire reader (also gathers temps from arduinos); logs to graphite.\n\n Last temps: %r" % self.settings.poller.lastDoc) + +if __name__ == '__main__': + log.setLevel(logging.DEBUG) + poller = Poller() + poller.sendTemps() + reactor.listenTCP(9078, cyclone.web.Application([ + (r'/', Index), + ], poller=poller)) + + LoopingCall(poller.sendTemps).start(interval=120) + reactor.run()