Mercurial > code > home > repos > homeauto
changeset 1309:b54164bcded3
rm old code to read a shaft encoder on an rpi, and a simple thermostat service that switches a house heater on
Ignore-this: b199393215c152f5daf5f0c781387d51
darcs-hash:3e25f2056470b710fb1cf5bfc34f585914469d56
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Sun, 21 Apr 2019 03:18:45 -0700 |
parents | 68c04c74db71 |
children | 68e172d9791e |
files | service/thermostat/rpi_buttons.py service/thermostat/thermostat.py |
diffstat | 2 files changed, 0 insertions(+), 218 deletions(-) [+] |
line wrap: on
line diff
--- a/service/thermostat/rpi_buttons.py Sun Apr 21 03:14:14 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -#!/usr/bin/python3 -""" -read button and knob on rpi, send back to thermostat program -""" -from RPi.GPIO import setmode, PUD_UP, setup, BCM, IN, input as pinInput -import time, json -# using a copy of urllib/request.py from py3.3 which supports 'method' -from request import urlopen, Request - -requestedTemperature = 'http://bang.bigasterisk.com:10001/requestedTemperature' -requestOpts = dict(headers={'user-agent': 'rpi buttons'}) - -def getRequestedTemp(): - return (json.loads(urlopen( - Request(requestedTemperature, **requestOpts)).read().decode('utf-8')) - )['tempF'] - -def setRequestedTemp(f): - urlopen(Request(url=requestedTemperature, - method='PUT', - data=json.dumps({'tempF' : f}).encode('utf-8'), - **requestOpts)) - -PIN_KNOB_A = 1 -PIN_KNOB_B = 4 -PIN_BUTTON = 0 - -setmode(BCM) -setup(PIN_KNOB_A, IN, PUD_UP) -setup(PIN_KNOB_B, IN, PUD_UP) -setup(PIN_BUTTON, IN, PUD_UP) - -print("reading knob and button, writing to %s" % requestedTemperature) -prev = None -prevButton = 0 -buttonHold = 0 -step = .02 -while True: - a, b = pinInput(PIN_KNOB_A), pinInput(PIN_KNOB_B) - button = not pinInput(PIN_BUTTON) - pos = (b * 2) + (a ^ b) - - if prev is None: - prev = pos - dpos = (pos - prev) % 4 - - if dpos == 1: - print ("up") - setRequestedTemp(getRequestedTemp() + 1) - elif dpos == -1 % 4: - print ("down") - setRequestedTemp(getRequestedTemp() - 1) - else: - pass # 0 or unknown - prev = pos - - if button: - buttonHold += 1 - if buttonHold == int(.1 / step): - print ("button to", button) - else: - buttonHold = 0 - - time.sleep(step)
--- a/service/thermostat/thermostat.py Sun Apr 21 03:14:14 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +0,0 @@ -from __future__ import division -""" -drives the heater output pin according to a requested temperature that you can edit. The temp is stored in mongodb. -""" -import cyclone.web, sys, urllib, time, pymongo, json, datetime -from dateutil.tz import tzlocal -from cyclone.httpclient import fetch -from twisted.internet import reactor -from twisted.internet.defer import inlineCallbacks, returnValue -from twisted.internet.task import LoopingCall -sys.path.append("/my/proj/homeauto/lib") -from cycloneerr import PrettyErrorHandler -from logsetup import log - -db = pymongo.Connection("bang")['thermostat'] - -@inlineCallbacks -def http(method, url, body=None): - resp = (yield fetch(url, method=method, postdata=body, - headers={'user-agent': ['thermostat.py']})) - if resp.code != 200: - raise ValueError("%s returned %s: %s" % (url, resp.code, resp.body)) - returnValue(resp.body) - -class Unknown(object): - pass - -class Therm(object): - def __init__(self): - self._lastOn = Unknown - self._lastOff = time.time() - 1000 - - # the non-logging path - self.graphite = 'http://bang:9037/render' - - # get this from devices.n3 - self.heaterPin = 'http://bang:9056/pin/d4' - - def getRequest(self): - return (db['request'].find_one(sort=[('t', -1)]) or - {'tempF':60} - )['tempF'] - - def setRequest(self, f): - db['request'].insert({'tempF': f, 't':datetime.datetime.now(tzlocal())}) - self.step() - http('POST', 'http://bang:9102/refreshTemperature') - # magma might also like to know - - @inlineCallbacks - def step(self): - roomF = yield self.getRoomTempF() - requestedF = self.getRequest() - active = yield self.active() - # bug here where, if something else turned off the heater, we - # don't count minsOff right - minsOff = self.minutesSinceOff() - minsOn = self.minutesSinceOn() - - log.info("roomF=%(roomF)s requestedF=%(requestedF)s active=%(active)s " - "minsOn=%(minsOn)s minsOff=%(minsOff)s" % vars()) - if not active: - if roomF < requestedF - 1: - if minsOff > 5: - log.info("start heater") - self.startCycle() - else: - log.info("wait to start") - else: - pass - else: - if roomF > requestedF + 1: - log.info("stop heater") - self.stopCycle() - elif minsOn > 50: - log.info("heater on too long- stopping") - self.stopCycle() - else: - log.info("ok to keep warming") - - @inlineCallbacks - def getRoomTempF(self): - target = 'system.house.temp.livingRoom' - body = (yield http('GET', self.graphite + '?' + - urllib.urlencode({ - 'target':"keepLastValue(%s)" % target, - 'rawData':'true', - 'from':'-60minutes', - }))) - latest = float(body.split(',')[-1]) - returnValue(latest) - - @inlineCallbacks - def active(self): - ret = yield http('GET', self.heaterPin) - returnValue(bool(int(ret.strip()))) - - @inlineCallbacks - def stopCycle(self): - log.info("heater off") - # need to make it be an output! - yield http('PUT', self.heaterPin, body='0') - self._lastOff = time.time() - - @inlineCallbacks - def startCycle(self): - log.info("heater on") - yield http('PUT', self.heaterPin, body='1') - self._lastOn = time.time() - - def minutesSinceOff(self): - if self._lastOff is Unknown: - self._lastOff = time.time() - return 0 - return (time.time() - self._lastOff) / 60 - - def minutesSinceOn(self): - if self._lastOn is Unknown: - self._lastOn = time.time() - return 0 - return (time.time() - self._lastOn) / 60 - - -class Index(PrettyErrorHandler, cyclone.web.RequestHandler): - def get(self): - self.write("thermostat. requested temp is %s." % - self.settings.therm.getRequest()) - -class RequestedTemperature(PrettyErrorHandler, cyclone.web.RequestHandler): - def get(self): - self.write(json.dumps({"tempF" : self.settings.therm.getRequest()})) - def put(self): - f = json.loads(self.request.body)['tempF'] - if not isinstance(f, (int, float)): - raise TypeError("tempF was %r" % f) - self.settings.therm.setRequest(f) - self.write("ok") - -if __name__ == '__main__': - t = Therm() - def step(): - try: - t.step() - except Exception, e: - log.warn(e) - - LoopingCall(step).start(interval=30) - - reactor.listenTCP(10001, cyclone.web.Application([ - (r'/', Index), - (r'/requestedTemperature', RequestedTemperature), - ], therm=t)) - - reactor.run()