annotate service/thermostat/thermostat.py @ 162:bb70eaa45666

whitespace Ignore-this: 29ddf8925c373bdb1ec8f62a01f0ac4f
author drewp@bigasterisk.com
date Sun, 22 Mar 2015 00:41:55 -0700
parents cbc557c35121
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
56
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
1 from __future__ import division
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
2 """
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
3 drives the heater output pin according to a requested temperature that you can edit. The temp is stored in mongodb.
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
4 """
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
5 import cyclone.web, sys, urllib, time, pymongo, json, datetime
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
6 from dateutil.tz import tzlocal
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
7 from cyclone.httpclient import fetch
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
8 from twisted.internet import reactor
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
9 from twisted.internet.defer import inlineCallbacks, returnValue
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
10 from twisted.internet.task import LoopingCall
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
11 sys.path.append("/my/proj/homeauto/lib")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
12 from cycloneerr import PrettyErrorHandler
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
13 from logsetup import log
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
14
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
15 db = pymongo.Connection("bang")['thermostat']
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
16
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
17 @inlineCallbacks
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
18 def http(method, url, body=None):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
19 resp = (yield fetch(url, method=method, postdata=body,
65
cbc557c35121 faster thermostat button polling. send updates to wallscreen (directly)
drewp@bigasterisk.com
parents: 56
diff changeset
20 headers={'user-agent': ['thermostat.py']}))
56
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
21 if resp.code != 200:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
22 raise ValueError("%s returned %s: %s" % (url, resp.code, resp.body))
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
23 returnValue(resp.body)
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
24
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
25 class Unknown(object):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
26 pass
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
27
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
28 class Therm(object):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
29 def __init__(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
30 self._lastOn = Unknown
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
31 self._lastOff = time.time() - 1000
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
32
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
33 # the non-logging path
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
34 self.graphite = 'http://bang:9037/render'
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
35
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
36 # get this from devices.n3
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
37 self.heaterPin = 'http://bang:9056/pin/d4'
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
38
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
39 def getRequest(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
40 return (db['request'].find_one(sort=[('t', -1)]) or
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
41 {'tempF':60}
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
42 )['tempF']
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
43
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
44 def setRequest(self, f):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
45 db['request'].insert({'tempF': f, 't':datetime.datetime.now(tzlocal())})
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
46 self.step()
65
cbc557c35121 faster thermostat button polling. send updates to wallscreen (directly)
drewp@bigasterisk.com
parents: 56
diff changeset
47 http('POST', 'http://bang:9102/refreshTemperature')
cbc557c35121 faster thermostat button polling. send updates to wallscreen (directly)
drewp@bigasterisk.com
parents: 56
diff changeset
48 # magma might also like to know
56
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
49
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
50 @inlineCallbacks
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
51 def step(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
52 roomF = yield self.getRoomTempF()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
53 requestedF = self.getRequest()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
54 active = yield self.active()
65
cbc557c35121 faster thermostat button polling. send updates to wallscreen (directly)
drewp@bigasterisk.com
parents: 56
diff changeset
55 # bug here where, if something else turned off the heater, we
cbc557c35121 faster thermostat button polling. send updates to wallscreen (directly)
drewp@bigasterisk.com
parents: 56
diff changeset
56 # don't count minsOff right
56
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
57 minsOff = self.minutesSinceOff()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
58 minsOn = self.minutesSinceOn()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
59
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
60 log.info("roomF=%(roomF)s requestedF=%(requestedF)s active=%(active)s "
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
61 "minsOn=%(minsOn)s minsOff=%(minsOff)s" % vars())
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
62 if not active:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
63 if roomF < requestedF - 1:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
64 if minsOff > 5:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
65 log.info("start heater")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
66 self.startCycle()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
67 else:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
68 log.info("wait to start")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
69 else:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
70 pass
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
71 else:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
72 if roomF > requestedF + 1:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
73 log.info("stop heater")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
74 self.stopCycle()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
75 elif minsOn > 50:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
76 log.info("heater on too long- stopping")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
77 self.stopCycle()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
78 else:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
79 log.info("ok to keep warming")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
80
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
81 @inlineCallbacks
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
82 def getRoomTempF(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
83 target = 'system.house.temp.livingRoom'
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
84 body = (yield http('GET', self.graphite + '?' +
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
85 urllib.urlencode({
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
86 'target':"keepLastValue(%s)" % target,
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
87 'rawData':'true',
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
88 'from':'-60minutes',
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
89 })))
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
90 latest = float(body.split(',')[-1])
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
91 returnValue(latest)
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
92
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
93 @inlineCallbacks
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
94 def active(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
95 ret = yield http('GET', self.heaterPin)
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
96 returnValue(bool(int(ret.strip())))
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
97
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
98 @inlineCallbacks
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
99 def stopCycle(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
100 log.info("heater off")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
101 # need to make it be an output!
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
102 yield http('PUT', self.heaterPin, body='0')
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
103 self._lastOff = time.time()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
104
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
105 @inlineCallbacks
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
106 def startCycle(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
107 log.info("heater on")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
108 yield http('PUT', self.heaterPin, body='1')
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
109 self._lastOn = time.time()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
110
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
111 def minutesSinceOff(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
112 if self._lastOff is Unknown:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
113 self._lastOff = time.time()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
114 return 0
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
115 return (time.time() - self._lastOff) / 60
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
116
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
117 def minutesSinceOn(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
118 if self._lastOn is Unknown:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
119 self._lastOn = time.time()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
120 return 0
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
121 return (time.time() - self._lastOn) / 60
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
122
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
123
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
124 class Index(PrettyErrorHandler, cyclone.web.RequestHandler):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
125 def get(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
126 self.write("thermostat. requested temp is %s." %
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
127 self.settings.therm.getRequest())
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
128
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
129 class RequestedTemperature(PrettyErrorHandler, cyclone.web.RequestHandler):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
130 def get(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
131 self.write(json.dumps({"tempF" : self.settings.therm.getRequest()}))
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
132 def put(self):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
133 f = json.loads(self.request.body)['tempF']
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
134 if not isinstance(f, (int, float)):
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
135 raise TypeError("tempF was %r" % f)
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
136 self.settings.therm.setRequest(f)
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
137 self.write("ok")
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
138
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
139 if __name__ == '__main__':
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
140 t = Therm()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
141 def step():
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
142 try:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
143 t.step()
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
144 except Exception, e:
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
145 log.warn(e)
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
146
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
147 LoopingCall(step).start(interval=30)
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
148
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
149 reactor.listenTCP(10001, cyclone.web.Application([
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
150 (r'/', Index),
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
151 (r'/requestedTemperature', RequestedTemperature),
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
152 ], therm=t))
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
153
517bd1407ed6 thermostat program and raspberry pi buttons reader
drewp@bigasterisk.com
parents:
diff changeset
154 reactor.run()