Login | ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/alarming/trunk/Server/daemon.py
Revision: 4
Committed: Tue May 7 09:36:39 2019 UTC (16 months, 3 weeks ago) by daniel-marschall
Content type: text/x-python
File size: 6922 byte(s)
Log Message:
Notifications now sent via threads

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

Properties

Name Value
svn:executable *