comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:6fd208b97616
1 """
2 talks to the arduino outside the front door. Don't write straight to
3 this LCD; use frontDoorMessage for that.
4
5 lcd is this wide
6 |-------------------|
7 22:05 85F in, 71F out
8
9 """
10
11 from __future__ import division
12
13 import cyclone.web, json, traceback, os, sys
14 from twisted.python import log
15 from twisted.internet import reactor, task
16 from twisted.web.client import getPage
17
18 sys.path.append("/my/proj/house/frontdoor")
19 from loggingserial import LoggingSerial
20
21 sys.path.append("/my/proj/homeauto/lib")
22 from cycloneerr import PrettyErrorHandler
23
24 class Board(object):
25 """
26 arduino board actions, plus the last values we wrote to it
27 """
28 def __init__(self, port):
29 self.ser = LoggingSerial(port=port)
30 self.ser.flush()
31
32 self.ser.write("\xff\x00\x00")
33 self.ser.write("\xff\x03\x00")
34 self.currentText = ""
35 self.currentBrightness = 0
36
37 def ping(self):
38 self.getDoor()
39
40 def getDoor(self):
41 self.ser.write("\xff\x01")
42 ret = self.ser.readJson()
43 return ret['door']
44
45 def getLcd(self):
46 return self.currentText
47
48 def setLcd(self, txt):
49 """
50 up to 8*21 chars
51 """
52 self.currentText = txt
53 self.ser.write("\xff\x00" + txt + "\x00")
54
55 def getLcdBrightness(self):
56 return self.currentBrightness
57
58 def setLcdBrightness(self, b):
59 """b in 0 to 255"""
60 self.currentBrightness = b
61 self.ser.write("\xff\x03" + chr(b))
62
63 def getTemperature(self):
64 """returns parsed json from the board"""
65 self.ser.write("\xff\x02")
66 # this can take 1.25 seconds per retry
67 f = self.ser.readJson()
68
69 if f['temp'] > 184 or f['temp'] < -100:
70 # this fails a lot, maybe 50% of the time. retry if
71 # you want
72 raise ValueError("out of range temp value (%s)" % f)
73 return f
74
75 class index(PrettyErrorHandler, cyclone.web.RequestHandler):
76 def get(self):
77 self.settings.board.ping()
78
79 self.set_header("Content-Type", "application/xhtml+xml")
80 self.write(open("index.html").read())
81
82 class Lcd(PrettyErrorHandler, cyclone.web.RequestHandler):
83 def get(self):
84 self.set_header("Content-Type", "text/plain")
85 self.write(self.settings.board.getLcd())
86
87 def put(self):
88 self.settings.board.setLcd(self.request.body)
89 self.set_status(204)
90
91 class Backlight(PrettyErrorHandler, cyclone.web.RequestHandler):
92 def get(self):
93 self.set_header("Content-Type", "application/json")
94 self.write(json.dumps({
95 "backlight" : self.settings.board.getLcdBrightness()}))
96
97 def put(self):
98 """param brightness=0 to brightness=255"""
99 self.settings.board.setLcdBrightness(
100 int(self.get_argument('brightness')))
101 self.write("ok")
102 post = put
103
104 class Door(PrettyErrorHandler, cyclone.web.RequestHandler):
105 def get(self):
106 self.set_header("Content-Type", "text/plain")
107 self.write(self.settings.board.getDoor())
108
109 class Temperature(PrettyErrorHandler, cyclone.web.RequestHandler):
110 def get(self):
111 f = self.settings.board.getTemperature()
112 self.set_header("Content-Type", "application/json")
113 self.write(f)
114
115 class Application(cyclone.web.Application):
116 def __init__(self, board):
117 handlers = [
118 (r"/", index),
119 (r'/lcd', Lcd),
120 (r'/door', Door),
121 (r'/temperature', Temperature),
122 (r'/lcd/backlight', Backlight),
123 ]
124 settings = {"board" : board}
125 cyclone.web.Application.__init__(self, handlers, **settings)
126
127
128 class Poller(object):
129 def __init__(self, board, postUrl, boardName):
130 self.board = board
131 self.postUrl = postUrl
132 self.boardName = boardName
133 self.last = None
134
135 def poll(self):
136 try:
137 new = self.board.getDoor()
138 if new != self.last:
139 msg = json.dumps(dict(board=self.boardName,
140 name="frontDoor", state=new))
141 getPage(self.postUrl,
142 method="POST",
143 postdata=msg,
144 headers={'Content-Type' : 'application/json'}
145 ).addErrback(self.reportError, msg)
146
147 self.last = new
148 except (IOError, OSError):
149 os.abort()
150 except Exception, e:
151 print "poll error", e
152 traceback.print_exc()
153
154 def reportError(self, msg, *args):
155 print "post error", msg, args
156
157 if __name__ == '__main__':
158
159 port = '/dev/ttyUSB0'
160 if not os.path.exists(port):
161 port = '/dev/ttyUSB1'
162
163 config = { # to be read from a file
164 'arduinoPort': port,
165 'servePort' : 9080,
166 'pollFrequency' : 1,
167 'boardName' : 'frontDoor', # gets sent with updates
168 'doorChangePost' : 'http://bang.bigasterisk.com:9069/inputChange',
169 # todo: need options to preset inputs/outputs at startup
170 }
171
172 log.startLogging(sys.stdout)
173
174 board = Board(port=config['arduinoPort'])
175
176 p = Poller(board, config['doorChangePost'], config['boardName'])
177 task.LoopingCall(p.poll).start(1/config['pollFrequency'])
178 reactor.listenTCP(config['servePort'], Application(board))
179 reactor.run()