Mercurial > code > home > repos > homeauto
diff service/frontDoorArduino/frontDoorArduino.py @ 0:6fd208b97616
start
Ignore-this: e06ac598970a0d4750f588ab89f56996
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Mon, 01 Aug 2011 03:30:30 -0700 |
parents | |
children | d6c2b9b87f7b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/frontDoorArduino/frontDoorArduino.py Mon Aug 01 03:30:30 2011 -0700 @@ -0,0 +1,179 @@ +""" +talks to the arduino outside the front door. Don't write straight to +this LCD; use frontDoorMessage for that. + +lcd is this wide +|-------------------| +22:05 85F in, 71F out + +""" + +from __future__ import division + +import cyclone.web, json, traceback, os, sys +from twisted.python import log +from twisted.internet import reactor, task +from twisted.web.client import getPage + +sys.path.append("/my/proj/house/frontdoor") +from loggingserial import LoggingSerial + +sys.path.append("/my/proj/homeauto/lib") +from cycloneerr import PrettyErrorHandler + +class Board(object): + """ + arduino board actions, plus the last values we wrote to it + """ + def __init__(self, port): + self.ser = LoggingSerial(port=port) + self.ser.flush() + + self.ser.write("\xff\x00\x00") + self.ser.write("\xff\x03\x00") + self.currentText = "" + self.currentBrightness = 0 + + def ping(self): + self.getDoor() + + def getDoor(self): + self.ser.write("\xff\x01") + ret = self.ser.readJson() + return ret['door'] + + def getLcd(self): + return self.currentText + + def setLcd(self, txt): + """ + up to 8*21 chars + """ + self.currentText = txt + self.ser.write("\xff\x00" + txt + "\x00") + + def getLcdBrightness(self): + return self.currentBrightness + + def setLcdBrightness(self, b): + """b in 0 to 255""" + self.currentBrightness = b + self.ser.write("\xff\x03" + chr(b)) + + def getTemperature(self): + """returns parsed json from the board""" + self.ser.write("\xff\x02") + # this can take 1.25 seconds per retry + f = self.ser.readJson() + + if f['temp'] > 184 or f['temp'] < -100: + # this fails a lot, maybe 50% of the time. retry if + # you want + raise ValueError("out of range temp value (%s)" % f) + return f + +class index(PrettyErrorHandler, cyclone.web.RequestHandler): + def get(self): + self.settings.board.ping() + + self.set_header("Content-Type", "application/xhtml+xml") + self.write(open("index.html").read()) + +class Lcd(PrettyErrorHandler, cyclone.web.RequestHandler): + def get(self): + self.set_header("Content-Type", "text/plain") + self.write(self.settings.board.getLcd()) + + def put(self): + self.settings.board.setLcd(self.request.body) + self.set_status(204) + +class Backlight(PrettyErrorHandler, cyclone.web.RequestHandler): + def get(self): + self.set_header("Content-Type", "application/json") + self.write(json.dumps({ + "backlight" : self.settings.board.getLcdBrightness()})) + + def put(self): + """param brightness=0 to brightness=255""" + self.settings.board.setLcdBrightness( + int(self.get_argument('brightness'))) + self.write("ok") + post = put + +class Door(PrettyErrorHandler, cyclone.web.RequestHandler): + def get(self): + self.set_header("Content-Type", "text/plain") + self.write(self.settings.board.getDoor()) + +class Temperature(PrettyErrorHandler, cyclone.web.RequestHandler): + def get(self): + f = self.settings.board.getTemperature() + self.set_header("Content-Type", "application/json") + self.write(f) + +class Application(cyclone.web.Application): + def __init__(self, board): + handlers = [ + (r"/", index), + (r'/lcd', Lcd), + (r'/door', Door), + (r'/temperature', Temperature), + (r'/lcd/backlight', Backlight), + ] + settings = {"board" : board} + cyclone.web.Application.__init__(self, handlers, **settings) + + +class Poller(object): + def __init__(self, board, postUrl, boardName): + self.board = board + self.postUrl = postUrl + self.boardName = boardName + self.last = None + + def poll(self): + try: + new = self.board.getDoor() + if new != self.last: + msg = json.dumps(dict(board=self.boardName, + name="frontDoor", state=new)) + getPage(self.postUrl, + method="POST", + postdata=msg, + headers={'Content-Type' : 'application/json'} + ).addErrback(self.reportError, msg) + + self.last = new + except (IOError, OSError): + os.abort() + except Exception, e: + print "poll error", e + traceback.print_exc() + + def reportError(self, msg, *args): + print "post error", msg, args + +if __name__ == '__main__': + + port = '/dev/ttyUSB0' + if not os.path.exists(port): + port = '/dev/ttyUSB1' + + config = { # to be read from a file + 'arduinoPort': port, + 'servePort' : 9080, + 'pollFrequency' : 1, + 'boardName' : 'frontDoor', # gets sent with updates + 'doorChangePost' : 'http://bang.bigasterisk.com:9069/inputChange', + # todo: need options to preset inputs/outputs at startup + } + + log.startLogging(sys.stdout) + + board = Board(port=config['arduinoPort']) + + p = Poller(board, config['doorChangePost'], config['boardName']) + task.LoopingCall(p.poll).start(1/config['pollFrequency']) + reactor.listenTCP(config['servePort'], Application(board)) + reactor.run()