Subversion Repositories alarming

Rev

Rev 4 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. #!/usr/bin/env python
  2.  
  3. from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
  4. import SocketServer
  5. from urlparse import parse_qs
  6. import os
  7. import cgi
  8. import time
  9. import requests
  10. import subprocess
  11. import config
  12.  
  13. g_subscribed = []
  14.  
  15. class S(BaseHTTPRequestHandler):
  16.     def _output(self, code, content):
  17.         self.send_response(code)
  18.         self.send_header('Content-type', 'text/html')
  19.         self.end_headers()
  20.         self.wfile.write(content)
  21.  
  22.     def do_GET(self):
  23.         try:
  24.                 action = parse_qs(self.path[2:]).get("action")[0]
  25.         except:
  26.                 action = None
  27.  
  28.         output = '''<html>
  29.  
  30. <head>
  31.         <title>Motion camera</title>
  32. </head>
  33.  
  34. <body onload="onload()">
  35.  
  36. <script>
  37.  
  38. function sleep (time) {
  39.         return new Promise((resolve) => setTimeout(resolve, time));
  40. }
  41.  
  42. function _toggle_alarm(st) {
  43.         document.getElementById("pleasewait").innerHTML = ' <i>Bitte warten</i>...';
  44.         var xhr = new XMLHttpRequest();
  45.         xhr.onreadystatechange = function () {
  46.                 var DONE = 4; // readyState 4 means the request is done.
  47.                 var OK = 200; // status 200 is a successful return.
  48.                 if (xhr.readyState === DONE) {
  49.                         if (xhr.status === OK) {
  50.                                 sleep(5000).then(() => {
  51.                                         document.location.reload();
  52.                                 });
  53.                         } else {
  54.                                 alert('Error: ' + xhr.status); // An error occurred during the request.
  55.                         }
  56.                 }
  57.         };
  58.  
  59.         var data = new FormData();
  60.         data.append('action', st ? 'motion_on'/*1.3.6.1.4.1.37476.2.4.1.100*/ : 'motion_off'/*1.3.6.1.4.1.37476.2.4.1.101*/);
  61.         xhr.open('POST', '/', true);
  62.         xhr.send(data);
  63. }
  64.  
  65. function onload() {
  66.         document.getElementById('campic').src = 'http://' + window.location.hostname + ':'''+str(config.motion_stream_port)+'''/';
  67. }
  68.  
  69. </script>'''
  70.  
  71.         if ismotionrunning():
  72.                 output = output + '<h2>Motion detection ON</h2>';
  73.                 output = output + '<p><a href="javascript:_toggle_alarm(0)">Disable motion detection</a><span id="pleasewait"></span></p>';
  74.                 output = output + '<p><img id="campic" src="" alt="Camera picture"></p>';
  75.         else:
  76.                 output = output + '<h2>Motion detection OFF</h2>';
  77.                 output = output + '<p><a href="javascript:_toggle_alarm(1)">Enable motion detection</a><span id="pleasewait"></span></p>';
  78.  
  79.         output = output + '<h2>Subscribers</h2>'
  80.  
  81.         found_subs = 0
  82.         for x in g_subscribed[:]:
  83.                 if int(time.time()) > x[2]:
  84.                         g_subscribed.remove(x)
  85.                 else:
  86.                         found_subs = found_subs + 1
  87.                         output = output + "<p>{0}:{1}</p>".format(x[0], x[1])
  88.  
  89.         if found_subs == 0:
  90.                 output = output + '<p>None</p>'
  91.  
  92.         output = output + '</body>'
  93.         output = output + '</html>'
  94.  
  95.         self._output(200, output)
  96.  
  97.     def do_HEAD(self):
  98.         self._output(200, '')
  99.  
  100.     def do_POST(self):
  101.         # https://stackoverflow.com/questions/4233218/python-how-do-i-get-key-value-pairs-from-the-basehttprequesthandler-http-post-h
  102.         # TODO: do we need the cgi package, or can we use functions available in this class (e.g. self_parse_qs?)
  103.         ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
  104.         if ctype == 'multipart/form-data':
  105.                 postvars = cgi.parse_multipart(self.rfile, pdict)
  106.         elif ctype == 'application/x-www-form-urlencoded':
  107.                 length = int(self.headers.getheader('content-length'))
  108.                 postvars = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1)
  109.         else:
  110.                 postvars = {}
  111.  
  112.         # ---
  113.  
  114.         global g_subscribed
  115.  
  116.         if pvget(postvars, "action")[0] == "client_subscribe": # 1.3.6.1.4.1.37476.2.4.1.1
  117.                 client_ip      = self.client_address[0]
  118.                 client_port    = pvget(postvars, "port")[0]
  119.                 client_ttl     = pvget(postvars, "ttl")[0]
  120.                 client_targets = pvget(postvars, "targets")
  121.  
  122.                 client_expires = int(time.time()) + int(client_ttl)
  123.  
  124.                 print "Client subscribed: {0}:{1}, searching for targets {2}".format(client_ip, client_port, client_targets)
  125.  
  126.                 # Remove all expired entries, and previous entries of that client
  127.                 for x in g_subscribed[:]:
  128.                         if int(time.time()) > x[2]:
  129.                                 g_subscribed.remove(x)
  130.                         elif (x[0] == client_ip) and (x[1] == client_port) and (x[3] == client_targets):
  131.                                 g_subscribed.remove(x)
  132.  
  133.                 # Now add our new client
  134.                 g_subscribed.append([client_ip, client_port, client_expires, client_targets])
  135.  
  136.                 # Send parameters of the device(s)
  137.                 d = {"action": "client_alert", # 1.3.6.1.4.1.37476.2.4.1.3
  138.                      "targets": ['1.3.6.1.4.1.37476.2.4.2.0', '1.3.6.1.4.1.37476.2.4.2.1002'],
  139.                      "motion_port": config.motion_stream_port,
  140.                      "simulation": "1"}
  141.                 requests.post("http://{0}:{1}".format(client_ip, client_port), data=d)
  142.  
  143.         if pvget(postvars, "action")[0] == "server_alert": # 1.3.6.1.4.1.37476.2.4.1.2
  144.                 server_targets = pvget(postvars, "targets")
  145.  
  146.                 found_g = 0
  147.  
  148.                 # TODO: this should be done in parallel threads, so that we can notify every client as fast as possible!
  149.                 for x in g_subscribed:
  150.                         client_ip      = x[0]
  151.                         client_port    = x[1]
  152.                         client_expires = x[2]
  153.                         client_targets = x[3]
  154.                         found_c = 0
  155.                         for st in server_targets:
  156.                                 for ct in client_targets:
  157.                                         if ct == st:
  158.                                                 found_c = found_c + 1
  159.                                                 found_g = found_g + 1
  160.                         if found_c > 0:
  161.                                 print "ALERT: Will alert client http://{0}:{1} and tell that targets {2} sent an alert".format(client_ip, client_port, server_targets)
  162.                                 d = {"action": "client_alert", # 1.3.6.1.4.1.37476.2.4.1.3
  163.                                      "targets": server_targets,
  164.                                      "motion_port": config.motion_stream_port,
  165.                                      "simulation": "0"}
  166.                                 requests.post("http://{0}:{1}".format(client_ip, client_port), data=d)
  167.  
  168.                 if found_g == 0:
  169.                         print "ALERT {0}, but nobody is listening!".format(server_targets)
  170.  
  171.  
  172.         if pvget(postvars, "action")[0] == "motion_on": # 1.3.6.1.4.1.37476.2.4.1.100
  173.                 print "Motion start"
  174.                 os.system(os.path.dirname(os.path.abspath(__file__)) + "/motion/motion_start_safe")
  175.  
  176.         if pvget(postvars, "action")[0] == "motion_off": # 1.3.6.1.4.1.37476.2.4.1.101
  177.                 print "Motion stop"
  178.                 os.system(os.path.dirname(os.path.abspath(__file__)) + "/motion/motion_stop_safe")
  179.  
  180.         self._output(200, '')
  181.  
  182. def pvget(ary, key):
  183.         if ary.get(key) == None:
  184.                 return [""]
  185.         else:
  186.                 return ary.get(key)
  187.  
  188. def run(server_class=HTTPServer, handler_class=S, port=8085):
  189.         server_address = ('', port)
  190.         httpd = server_class(server_address, handler_class)
  191.         print 'Starting httpd...'
  192.         httpd.serve_forever()
  193.  
  194. def ismotionrunning():
  195.         p = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
  196.         out, err = p.communicate()
  197.         try:
  198.                 return ('/usr/bin/motion' in str(out))
  199.         except:
  200.                 res = os.system("pidof -x /usr/bin/motion > /dev/null")
  201.                 return (res >> 8) == 0
  202.  
  203. if __name__ == "__main__":
  204.         from sys import argv
  205.  
  206.         if len(argv) == 2:
  207.                 run(port=int(argv[1]))
  208.         else:
  209.                 run()
  210.  
  211.