Login | ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/alarming/trunk/Server/daemon.py
Revision: 3
Committed: Mon May 6 21:25:33 2019 UTC (16 months, 3 weeks ago) by daniel-marschall
Content type: text/x-python
File size: 6383 byte(s)
Log Message:
Initial release to SVN

File Contents

# User Rev Content
1 daniel-marschall 3 #!/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    

Properties

Name Value
svn:executable *