lots of updates, amongst others:
[saartuer.git] / ringd
diff --git a/ringd b/ringd
index 3835d9c80e868e0f0b743d46faafc50a4d7fb31d..201322f4f640ff80f46d594e1946578e5b08ec12 100755 (executable)
--- a/ringd
+++ b/ringd
@@ -1,37 +1,99 @@
 #!/usr/bin/python3
 import time, socket, atexit
+import queue, threading, select
+from libtuer import log
 import RPi.GPIO as GPIO
+GPIO.setmode(GPIO.BOARD)
+atexit.register(GPIO.cleanup)
 
 tuerSock = "/run/tuer.sock"
-
 ringPin = 18
 
-GPIO.setmode(GPIO.BOARD)
-GPIO.setup(ringPin, GPIO.IN)
-atexit.register(GPIO.cleanup);
-
-lastEvent = 0
-
+# Main classes
+class PinWatcher():
+       def __init__(self, pin, histlen):
+               GPIO.setup(pin, GPIO.IN)
+               assert histlen > 1 # otherwise our logic goes nuts...
+               self._pin = pin
+               self._histlen = histlen
+               # state change detection
+               self._state = None
+               self._newstate = None # != None iff we are currently seeing a state change
+               self._newstatelen = 0 # only valid if newstate != None
+               # start state change handler thread
+               self._q = queue.Queue()
+               self._t = threading.Thread(target=self.queue_consumer)
+               self._t.start()
+       
+       def queue_consumer(self):
+               while True:
+                       el = self._q.get()
+                       if el is None: return # we are supposed to terminate
+                       # handle the state change
+                       (oldstate, newstate) = el
+                       self.callback(oldstate, newstate)
+       
+       def read(self):
+               curstate = GPIO.input(self._pin)
+               assert curstate in (0, 1)
+               if curstate != self._state:
+                       # the state is about to change
+                       if curstate == self._newstate:
+                               # we already saw this new state
+                               self._newstatelen += 1
+                               if self._newstatelen >= self._histlen:
+                                       self._q.put((self._state, curstate)) # send stuff to the other thread
+                                       self._state = curstate
+                                       self._newstate = None
+                       else:
+                               # now check for how long we see this new state
+                               self._newstate = curstate
+                               self._newstatelen = 1
+               else:
+                       # old state is preserved
+                       self._newstate = None
+       
+       def quit(self):
+               self._q.put(None)
+               self._t.join()
 
-while True:
-       GPIO.wait_for_edge(ringPin, GPIO.BOTH)
-       # measure time since event
-       now = time.time()
-       timePassed = now-lastEvent
-       print("Time between events %f" % timePassed)
-       # remember, remember
-       lastEvent = now
-       # action to be taken?
-       if timePassed >= 1.5 and timePassed <= 3:
-               print("Opening door")
+class RingWatcher(PinWatcher):
+       def __init__(self):
+               super().__init__(ringPin, 2)
+               self.last1Event = None
+       
+       def callback(self, oldstate, newstate):
+               if oldstate is None:
+                       return # ignore the very first state change
+               # now (oldstate, newstate) is either (0, 1) or (1, 0)
+               if newstate:
+                       self.last1Event = time.time()
+               elif self.last1Event is not None:
+                       # how long was this pressed?
+                       timePressed = time.time() - self.last1Event
+                       log("Ring button pressed for",timePressed)
+                       if timePressed >= 1.5 and timePressed <= 3:
+                               self.buzz()
+       
+       def buzz(self):
+               log("Opening door")
                # talk with tuerd
                s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                s.connect(tuerSock)
                s.send(b'buzz')
-               data = s.recv(4)
                s.close()
-               print("...done")
-               if data != b'1':
-                       print("Received unexpected answer %s" % str(data))
 
+# MAIN PROGRAM
+pins = [
+       RingWatcher(),
+]
 
+try:
+       log("entering loop")
+       while True:
+               for pin in pins:
+                       pin.read()
+               time.sleep(0.02)
+except KeyboardInterrupt:
+       for pin in pins:
+               pin.quit()