Subversion Repositories alarming

Rev

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