Subversion Repositories alarming

Rev

Rev 3 | Rev 5 | Go to most recent revision | Details | Compare with Previous | 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
4 daniel-mar 12
import threading
3 daniel-mar 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
 
4 daniel-mar 29
        output = '''<!DOCTYPE html>
30
<html lang="en">
3 daniel-mar 31
 
32
<head>
4 daniel-mar 33
        <meta charset="UTF-8">
3 daniel-mar 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) {
4 daniel-mar 46
        document.getElementById("pleasewait").innerHTML = ' <i>Please wait...</i>';
3 daniel-mar 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*/);
4 daniel-mar 64
        xhr.open('POST', document.location, true);
3 daniel-mar 65
        xhr.send(data);
66
}
67
 
68
function onload() {
4 daniel-mar 69
        if (document.getElementById('campic') != null) {
70
                document.getElementById('campic').src = 'http://' + window.location.hostname + ':'''+str(config.motion_stream_port)+'''/';
71
        }
3 daniel-mar 72
}
73
 
74
</script>'''
75
 
76
        if ismotionrunning():
4 daniel-mar 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>';
3 daniel-mar 81
        else:
4 daniel-mar 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>'
3 daniel-mar 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
 
4 daniel-mar 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
 
3 daniel-mar 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
4 daniel-mar 116
        # Question: Do we need the cgi package, or can we use functions available in this class (e.g. self_parse_qs?)
3 daniel-mar 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
 
4 daniel-mar 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]
3 daniel-mar 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:
4 daniel-mar 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()
3 daniel-mar 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):
4 daniel-mar 198
        print 'Starting server, listening to port {0}...'.format(port)
3 daniel-mar 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