python
[svn42.git] / door_daemon.py
diff --git a/door_daemon.py b/door_daemon.py
new file mode 100755 (executable)
index 0000000..903d6cf
--- /dev/null
@@ -0,0 +1,183 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+import os
+import sys
+import threading
+import logging
+import urllib
+import time
+import signal
+import re
+
+logging.basicConfig(level=logging.INFO,filename='/var/log/tuer.log')
+
+class StatusDisplay(object):
+   def __init__(self):
+    self.url_open = 'https://www.realraum.at/cgi/status.cgi?pass=jako16&set=%3Chtml%3E%3Cbody%20bgcolor=%22lime%22%3E%3Ch3%3E%3Ccenter%3ETuer%20ist%20Offen%3C/center%3E%3C/h3%3E%3C/body%3E%3C/html%3E';
+    self.url_closed = 'https://www.realraum.at/cgi/status.cgi?pass=jako16&set=%3Chtml%3E%3Cbody%20bgcolor=%22red%22%3E%3Ch3%3E%3Ccenter%3ETuer%20ist%20Geschlossen%3C/center%3E%3C/h3%3E%3C/body%3E%3C/html%3E';
+    self.last_status_set=self.url_open
+    #object.__init__(self)
+    
+    def display_open(self):
+      if self.last_status_set != self.url_open:
+        self.last_status_set=self.url_open
+        f = urllib.urlopen(self.last_status_set)
+        f.close()
+      
+    def display_closed(self):
+      if self.last_status_set != self.url_closed:
+        self.last_status_set=self.url_closed
+        f = urllib.urlopen(self.last_status_set)
+        f.close()
+
+
+class ArduinoUSBThread ( threading.Thread ):
+  def __init__(self, file_dev_ttyusb):
+    self.re_isidle = re.compile(r'open')
+    self.re_isopen = re.compile(r'open')
+    self.re_isclosed = re.compile(r'close|closing')
+    self.re_toolong = re.compile(r'took too long!')
+    self.min_seconds_between_reset=10;
+    self.timestamp_send_reset=0;
+    self.running=True
+    self.lastline=""
+    self.last_status=None
+    self.cv_updatestatus = threading.Condition(); #lock ist automatically created withing condition
+    self.file_dev_ttyusb=file_dev_ttyusb
+    self.statusdisplay = StatusDisplay()
+    threading.Thread.__init__(self)
+    
+  def stop(self):
+    self.running=False
+    self.fh.close()
+
+  def send_open(self):
+    self.send_statusrequest()
+    self.cv_updatestatus.acquire()
+    self.cv_updatestatus.wait(3.0)
+    self.cv_updatestatus.release()
+    if re_isidle.search(self.lastline):
+      logging.info("Opening Door")
+      self.fh.write("o");
+    
+  def send_close(self):
+    self.send_statusrequest()
+    self.cv_updatestatus.acquire()
+    self.cv_updatestatus.wait(3.0)
+    self.cv_updatestatus.release()    
+    if re_isidle.search(self.lastline):
+      logging.info("Closing Door")
+      self.fh.write("c");
+      
+  def send_toggle(self):
+    self.send_statusrequest()
+    self.cv_updatestatus.acquire()
+    self.cv_updatestatus.wait(3.0)
+    self.cv_updatestatus.release()
+    if re_isidle.search(self.lastline):
+      if self.last_status == "open":
+        logging.info("Closing Door")
+        self.fh.write("c");
+      elif self.last_status == "closed":
+        logging.info("Opening Door")
+        self.fh.write("o");
+      
+  def send_reset(self):
+    logging.info("Resetting Door")
+    self.fh.write("r");
+
+  def send_statusrequest(self):
+    self.fh.write("s");
+
+  def run (self):
+    self.fh = open(self.file_dev_ttyusb,"rw")
+    while (self.running):
+      print "."
+      line = self.fh.readline();
+      self.cv_updatestatus.acquire()
+      self.lastline=line
+      logging.info(self.file_dev_ttyusb+": "+self.lastline)
+      if self.re_isclosed.search(self.lastline):
+        self.last_status="closed"
+        self.statusdisplay.display_open()
+      elif self.re_isopen.search(self.lastline):
+        self.last_status="open"
+        self.statusdisplay.display_closed()
+      elif self.re_toolong.search(self.lastline):
+        self.last_status="error"
+        if (time.time() - self.timestamp_send_reset) > self.min_seconds_between_reset:
+          self.timestamp_send_reset=time.time()
+          self.send_reset()
+      self.cv_updatestatus.notifyAll()
+      self.cv_updatestatus.release()
+    if self.fh:
+      self.fh.close()
+
+class ControlFIFOThread ( threading.Thread ):
+  def __init__(self, file_fifo, arduino):
+    self.running=True
+    self.file_fifo=file_fifo
+    self.arduino = arduino
+    self.re_cmd = re.compile(r'^(\w+)\s*(.*)')
+    threading.Thread.__init__(self)
+  
+  def stop(self):
+    self.running=False
+    self.fh.close()
+  
+  def run (self):
+    self.fh = open(self.file_fifo,"r")
+    while (self.running):
+      print "."
+      line=self.fh.readline()
+      m = self.re_cmd.match(line)
+      if not m is None:
+        (cmd,who) = m.group(1,2)
+        if cmd == "open":
+          logging.info("Open Requested by %s" % who)
+          arduino.send_open()
+        elif cmd == "close":
+          logging.info("Close Requested by %s" % who)
+          arduino.send_close()
+        elif cmd == "toggle":
+          logging.info("Toggle Requested by %s" % who)
+          arduino.send_toggle()
+        elif cmd == "reset":
+          logging.info("Reset Requested by %s" % who)
+          arduino.send_reset()
+        elif cmd == "status":
+          arduino.send_statusrequest()
+        elif cmd == "log":
+          logging.info(who)
+        else:
+          logging.info("Invalid Command %s %s" % (cmd,who))
+    if self.fh:
+      self.fh.close()
+
+
+
+fifofile = "/tmp/door_cmd.fifo"
+
+if (not os.path.exists(fifofile)):
+  os.system("mkfifo -m 600 $fifofile")
+  os.system("setfacl -m u:realraum:rw $fifofile")
+  os.system("setfacl -m u:asterisk:rw $fifofile")
+
+logging.info("Door Daemon started")
+
+arduino = ArduinoUSBThread("/dev/ttyUSB0")
+arduino.start()
+ctrlfifo = ControlFIFOThread(fifofile,arduino)
+ctrlfifo.start()
+
+def exit_handler(signum, frame):
+  global arduino, ctrlfifo
+  logging.info("Door Daemon stopping")
+  arduino.send_close()
+  ctrlfifo.stop()
+  arduino.stop()
+  sys.exit(0)
+  
+signal.signal(signal.SIGTERM, exit_handler)
+signal.signal(signal.SIGINT, exit_handler)
+signal.signal(signal.SIGQUIT, exit_handler)