Mercurial > code > home > repos > homeauto
annotate service/frontDoorArduino/frontDoorArduino.py @ 823:0aafd40e4afc
move more ports to use /dev/serial/by-id/
Ignore-this: 4d9bc18902dfe08897c3e03579c87b23
darcs-hash:20111204030320-312f9-da93888bb0e3b2bc898eb34f3126706d0ac3aefa.gz
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Sat, 03 Dec 2011 19:03:20 -0800 |
parents | b1c92ee20ff4 |
children | 0cffbf171a97 |
rev | line source |
---|---|
805 | 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 | |
822 | 9 pin 11 senses the door |
10 pin 12 activates the front yard lights ('out yard') | |
11 | |
805 | 12 """ |
13 | |
14 from __future__ import division | |
15 | |
16 import cyclone.web, json, traceback, os, sys | |
17 from twisted.python import log | |
18 from twisted.internet import reactor, task | |
19 from twisted.web.client import getPage | |
20 | |
21 sys.path.append("/my/proj/house/frontdoor") | |
22 from loggingserial import LoggingSerial | |
23 | |
24 sys.path.append("/my/proj/homeauto/lib") | |
25 from cycloneerr import PrettyErrorHandler | |
26 | |
27 class Board(object): | |
28 """ | |
29 arduino board actions, plus the last values we wrote to it | |
30 """ | |
31 def __init__(self, port): | |
32 self.ser = LoggingSerial(port=port) | |
33 self.ser.flush() | |
34 | |
822 | 35 self.setLcd("") |
36 self.setLcdBrightness(0) | |
37 self.setYardLight(0) | |
805 | 38 |
39 def ping(self): | |
40 self.getDoor() | |
41 | |
42 def getDoor(self): | |
43 self.ser.write("\xff\x01") | |
44 ret = self.ser.readJson() | |
45 return ret['door'] | |
46 | |
822 | 47 def setYardLight(self, level): |
48 self.currentYardLight = bool(level) | |
49 self.ser.write("\xff\x04" + chr(bool(self.currentYardLight))) | |
50 | |
51 def getYardLight(self): | |
52 return self.currentYardLight | |
53 | |
805 | 54 def getLcd(self): |
55 return self.currentText | |
56 | |
57 def setLcd(self, txt): | |
58 """ | |
59 up to 8*21 chars | |
60 """ | |
61 self.currentText = txt | |
62 self.ser.write("\xff\x00" + txt + "\x00") | |
63 | |
64 def getLcdBrightness(self): | |
65 return self.currentBrightness | |
66 | |
67 def setLcdBrightness(self, b): | |
68 """b in 0 to 255""" | |
69 self.currentBrightness = b | |
70 self.ser.write("\xff\x03" + chr(b)) | |
71 | |
72 def getTemperature(self): | |
73 """returns parsed json from the board""" | |
74 self.ser.write("\xff\x02") | |
75 # this can take 1.25 seconds per retry | |
76 f = self.ser.readJson() | |
77 | |
78 if f['temp'] > 184 or f['temp'] < -100: | |
79 # this fails a lot, maybe 50% of the time. retry if | |
80 # you want | |
81 raise ValueError("out of range temp value (%s)" % f) | |
82 return f | |
83 | |
84 class index(PrettyErrorHandler, cyclone.web.RequestHandler): | |
85 def get(self): | |
86 self.settings.board.ping() | |
87 | |
88 self.set_header("Content-Type", "application/xhtml+xml") | |
89 self.write(open("index.html").read()) | |
90 | |
91 class Lcd(PrettyErrorHandler, cyclone.web.RequestHandler): | |
92 def get(self): | |
93 self.set_header("Content-Type", "text/plain") | |
94 self.write(self.settings.board.getLcd()) | |
95 | |
96 def put(self): | |
97 self.settings.board.setLcd(self.request.body) | |
98 self.set_status(204) | |
99 | |
100 class Backlight(PrettyErrorHandler, cyclone.web.RequestHandler): | |
101 def get(self): | |
102 self.set_header("Content-Type", "application/json") | |
103 self.write(json.dumps({ | |
104 "backlight" : self.settings.board.getLcdBrightness()})) | |
105 | |
106 def put(self): | |
107 """param brightness=0 to brightness=255""" | |
108 self.settings.board.setLcdBrightness( | |
109 int(self.get_argument('brightness'))) | |
110 self.write("ok") | |
111 post = put | |
112 | |
822 | 113 |
114 class YardLight(PrettyErrorHandler, cyclone.web.RequestHandler): | |
115 def get(self): | |
116 self.set_header("Content-Type", "application/json") | |
117 self.write(json.dumps({ | |
118 "yardLight" : self.settings.board.getYardLight()})) | |
119 | |
120 def put(self): | |
121 """text true or false or 0 or 1""" | |
122 self.settings.board.setYardLight( | |
123 self.request.body.strip() in ['true', '1']) | |
124 self.write("ok") | |
125 post = put | |
126 | |
127 | |
805 | 128 class Door(PrettyErrorHandler, cyclone.web.RequestHandler): |
129 def get(self): | |
130 self.set_header("Content-Type", "text/plain") | |
131 self.write(self.settings.board.getDoor()) | |
132 | |
133 class Temperature(PrettyErrorHandler, cyclone.web.RequestHandler): | |
134 def get(self): | |
135 f = self.settings.board.getTemperature() | |
136 self.set_header("Content-Type", "application/json") | |
137 self.write(f) | |
138 | |
139 class Application(cyclone.web.Application): | |
140 def __init__(self, board): | |
141 handlers = [ | |
142 (r"/", index), | |
143 (r'/lcd', Lcd), | |
144 (r'/door', Door), | |
145 (r'/temperature', Temperature), | |
146 (r'/lcd/backlight', Backlight), | |
822 | 147 (r'/yardLight', YardLight), |
805 | 148 ] |
149 settings = {"board" : board} | |
150 cyclone.web.Application.__init__(self, handlers, **settings) | |
151 | |
152 | |
153 class Poller(object): | |
154 def __init__(self, board, postUrl, boardName): | |
155 self.board = board | |
156 self.postUrl = postUrl | |
157 self.boardName = boardName | |
158 self.last = None | |
159 | |
160 def poll(self): | |
161 try: | |
162 new = self.board.getDoor() | |
163 if new != self.last: | |
164 msg = json.dumps(dict(board=self.boardName, | |
165 name="frontDoor", state=new)) | |
166 getPage(self.postUrl, | |
167 method="POST", | |
168 postdata=msg, | |
169 headers={'Content-Type' : 'application/json'} | |
170 ).addErrback(self.reportError, msg) | |
171 | |
172 self.last = new | |
173 except (IOError, OSError): | |
174 os.abort() | |
175 except Exception, e: | |
176 print "poll error", e | |
177 traceback.print_exc() | |
178 | |
179 def reportError(self, msg, *args): | |
180 print "post error", msg, args | |
181 | |
182 if __name__ == '__main__': | |
183 | |
184 config = { # to be read from a file | |
823
0aafd40e4afc
move more ports to use /dev/serial/by-id/
drewp <drewp@bigasterisk.com>
parents:
822
diff
changeset
|
185 'arduinoPort': '/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A6004bUG-if00-port0', |
805 | 186 'servePort' : 9080, |
187 'pollFrequency' : 1, | |
188 'boardName' : 'frontDoor', # gets sent with updates | |
189 'doorChangePost' : 'http://bang.bigasterisk.com:9069/inputChange', | |
190 # todo: need options to preset inputs/outputs at startup | |
191 } | |
192 | |
822 | 193 #log.startLogging(sys.stdout) |
805 | 194 |
195 board = Board(port=config['arduinoPort']) | |
196 | |
197 p = Poller(board, config['doorChangePost'], config['boardName']) | |
198 task.LoopingCall(p.poll).start(1/config['pollFrequency']) | |
199 reactor.listenTCP(config['servePort'], Application(board)) | |
200 reactor.run() |