Mercurial > code > home > repos > homeauto
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() |