Mercurial > code > home > repos > homeauto
comparison service/bedroomArduino/bedroomArduino.py @ 29:5fef0b7db346
bedroomarduino project started. web server isn't ready
Ignore-this: 892495965fde1ee17162505d50222f81
author | drewp@bigasterisk.com |
---|---|
date | Tue, 17 Apr 2012 21:53:19 -0700 |
parents | |
children | 8e55a6a9c425 |
comparison
equal
deleted
inserted
replaced
28:97d7c0bb9408 | 29:5fef0b7db346 |
---|---|
1 #!bin/python | |
2 """ | |
3 talks to bed.pde on an arduino | |
4 """ | |
5 | |
6 from __future__ import division | |
7 | |
8 import cyclone.web, json, traceback, os, sys, time, logging | |
9 from twisted.internet import reactor, task | |
10 from twisted.web.client import getPage | |
11 sys.path.append("/my/proj/house/frontdoor") | |
12 from loggingserial import LoggingSerial | |
13 sys.path.append("/my/proj/homeauto/lib") | |
14 from cycloneerr import PrettyErrorHandler | |
15 from logsetup import log | |
16 | |
17 sys.path.append("/my/site/magma") | |
18 from stategraph import StateGraph | |
19 from rdflib import Namespace | |
20 | |
21 ROOM = Namespace("http://projects.bigasterisk.com/room/") | |
22 DEV = Namespace("http://projects.bigasterisk.com/device/") | |
23 | |
24 class ArduinoBedroom(object): | |
25 def __init__(self, port='/dev/ttyACM0'): | |
26 self.ser = LoggingSerial(port=port, baudrate=115200, timeout=1) | |
27 self.ser.flush() | |
28 | |
29 def ping(self): | |
30 self.ser.write("\x60\x00\x00") | |
31 msg = self.ser.readJson() | |
32 assert msg == {"ok":True}, msg | |
33 | |
34 def poll(self): | |
35 self.ser.write("\x60\x01\x00") | |
36 ret = self.ser.readJson() | |
37 return ret | |
38 | |
39 def setSpeakerChoice(self, pillow): | |
40 self.ser.write("\x60\x02" + chr(pillow)) | |
41 return self.ser.readJson() | |
42 | |
43 class Index(PrettyErrorHandler, cyclone.web.RequestHandler): | |
44 def get(self): | |
45 """ | |
46 this is an acceptable status check since it makes a round-trip | |
47 to the arduino before returning success | |
48 """ | |
49 self.settings.arduino.ping() | |
50 | |
51 self.set_header("Content-Type", "application/xhtml+xml") | |
52 self.write(open("index.html").read()) | |
53 | |
54 class SpeakerChoice(PrettyErrorHandler, cyclone.web.RequestHandler): | |
55 def put(self): | |
56 ret = self.settings.arduino.setSpeakerChoice(int(self.get_argument('pillow'))) | |
57 self.write(ret) | |
58 | |
59 class GraphPage(PrettyErrorHandler, cyclone.web.RequestHandler): | |
60 def get(self): | |
61 self.set_header("Content-Type", "application/x-trig") | |
62 g = StateGraph(ROOM['bedroomArduino']) | |
63 self.settings.poller.assertIsCurrent() | |
64 g.add((DEV['bedroomMotion'], ROOM['state'], | |
65 ROOM['motion'] if self.settings.poller.lastValues['motion'] else | |
66 ROOM['noMotion'])) | |
67 self.write(g.asTrig()) | |
68 | |
69 class Poller(object): | |
70 """ | |
71 Watches sensor values | |
72 """ | |
73 def __init__(self, config, ard, period): | |
74 self.config, self.ard = config, ard | |
75 self.period = period | |
76 self.lastValues = None | |
77 self.lastPollTime = 0 | |
78 self.lastMotion = False | |
79 | |
80 def assertIsCurrent(self): | |
81 """raise an error if the poll data is not fresh""" | |
82 dt = time.time() - self.lastPollTime | |
83 if dt > period * 2: | |
84 raise ValueError("last poll time was too old: %.1f sec ago" % dt) | |
85 | |
86 def poll(self): | |
87 now = time.time() | |
88 try: | |
89 try: | |
90 newData = ard.poll() | |
91 except ValueError, e: | |
92 print e | |
93 else: | |
94 print newData | |
95 return | |
96 self.lastPollTime = now | |
97 self.lastValues = newData # for other data besides the blinks | |
98 self.processMotion(newData['motion']) | |
99 | |
100 except (IOError, OSError): | |
101 os.abort() | |
102 except Exception, e: | |
103 print "poll error", e | |
104 traceback.print_exc() | |
105 | |
106 def processMotion(self, state): | |
107 if state == self.lastMotion: | |
108 return | |
109 self.lastMotion = state | |
110 msg = json.dumps(dict(board=self.config['boardName'], | |
111 name="bedroomMotion", state=state)) | |
112 getPage('http://bang.bigasterisk.com:9069/inputChange', | |
113 method="POST", | |
114 postdata=msg, | |
115 headers={'Content-Type' : 'application/json'} | |
116 ).addErrback(self.reportError, msg) | |
117 | |
118 def reportError(self, msg, *args): | |
119 print "post error", msg, args | |
120 | |
121 if __name__ == '__main__': | |
122 | |
123 config = { # to be read from a file | |
124 'arduinoPort': '/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A4001lVK-if00-port0', | |
125 'servePort' : 9088, | |
126 'pollFrequency' : 6, | |
127 'boardName' : 'bedroom', # gets sent with updates | |
128 } | |
129 | |
130 from twisted.python import log as twlog | |
131 #twlog.startLogging(sys.stdout) | |
132 | |
133 log.setLevel(logging.DEBUG) | |
134 | |
135 ard = ArduinoBedroom(port=config['arduinoPort']) | |
136 | |
137 period = 1/config['pollFrequency'] | |
138 p = Poller(config, ard, period) | |
139 task.LoopingCall(p.poll).start(period) | |
140 | |
141 reactor.listenTCP(config['servePort'], cyclone.web.Application([ | |
142 (r"/", Index), | |
143 (r"/graph", GraphPage), | |
144 (r'/speakerChoice', SpeakerChoice), | |
145 ], arduino=ard, poller=p)) | |
146 reactor.run() |