Rev 4 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3 | daniel-mar | 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 |