Login | ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/alarming/trunk/Server/daemon.py
Revision: 5
Committed: Wed May 8 23:02:47 2019 UTC (16 months, 3 weeks ago) by daniel-marschall
Content type: text/x-python
File size: 7130 byte(s)
Log Message:
Bugfix

File Contents

# Content
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

Properties

Name Value
svn:executable *