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 subscriber in g_subscribed[:]: |
89 |
if int(time.time()) > subscriber[2]: |
90 |
g_subscribed.remove(subscriber) |
91 |
else: |
92 |
found_subs = found_subs + 1 |
93 |
output = output + "<p>{0}:{1}</p>".format(subscriber[0], subscriber[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 subscriber in g_subscribed[:]: |
142 |
if int(time.time()) > subscriber[2]: |
143 |
g_subscribed.remove(subscriber) |
144 |
elif (subscriber[0] == client_ip) and (subscriber[1] == client_port) and (subscriber[3] == client_targets): |
145 |
g_subscribed.remove(subscriber) |
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 |
|
168 |
if int(time.time()) > client_expires: |
169 |
g_subscribed.remove(subscriber) |
170 |
else: |
171 |
found_c = 0 |
172 |
for st in server_targets: |
173 |
for ct in client_targets: |
174 |
if ct == st: |
175 |
found_c = found_c + 1 |
176 |
found_g = found_g + 1 |
177 |
if found_c > 0: |
178 |
# Notify clients via threads, so that all clients are equally fast notified |
179 |
thread = threading.Thread(target=self.thr_client_notify, args=(client_ip, client_port, server_targets, )) |
180 |
thread.start() |
181 |
|
182 |
if found_g == 0: |
183 |
print "ALERT {0}, but nobody is listening!".format(server_targets) |
184 |
|
185 |
if pvget(postvars, "action")[0] == "motion_on": # 1.3.6.1.4.1.37476.2.4.1.100 |
186 |
print "Motion start" |
187 |
os.system(os.path.dirname(os.path.abspath(__file__)) + "/motion/motion_start_safe") |
188 |
|
189 |
if pvget(postvars, "action")[0] == "motion_off": # 1.3.6.1.4.1.37476.2.4.1.101 |
190 |
print "Motion stop" |
191 |
os.system(os.path.dirname(os.path.abspath(__file__)) + "/motion/motion_stop_safe") |
192 |
|
193 |
self._output(200, '') |
194 |
|
195 |
def pvget(ary, key): |
196 |
if ary.get(key) == None: |
197 |
return [""] |
198 |
else: |
199 |
return ary.get(key) |
200 |
|
201 |
def run(server_class=HTTPServer, handler_class=S, port=8085): |
202 |
print 'Starting server, listening to port {0}...'.format(port) |
203 |
server_address = ('', port) |
204 |
httpd = server_class(server_address, handler_class) |
205 |
httpd.serve_forever() |
206 |
|
207 |
def ismotionrunning(): |
208 |
p = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE) |
209 |
out, err = p.communicate() |
210 |
try: |
211 |
return ('/usr/bin/motion' in str(out)) |
212 |
except: |
213 |
res = os.system("pidof -x /usr/bin/motion > /dev/null") |
214 |
return (res >> 8) == 0 |
215 |
|
216 |
if __name__ == "__main__": |
217 |
from sys import argv |
218 |
|
219 |
if len(argv) == 2: |
220 |
run(port=int(argv[1])) |
221 |
else: |
222 |
run() |
223 |
|