Subversion Repositories alarming

Rev

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