Subversion Repositories alarming

Rev

Rev 8 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 8 Rev 10
1
#!/usr/bin/env python
1
#!/usr/bin/env python
2
 
2
 
3
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
3
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
4
import SocketServer
4
import SocketServer
5
from urlparse import parse_qs
5
from urlparse import parse_qs
6
import os
6
import os
7
import cgi
7
import cgi
8
import time
8
import time
9
import requests
9
import requests
10
import subprocess
10
import subprocess
11
import config
11
import config
12
import threading
12
import threading
13
import subprocess as sp
13
import subprocess as sp
14
 
14
 
15
g_subscribed = []
15
g_subscribed = []
16
g_bellListenerProc = None
16
g_bellListenerProc = None
17
 
17
 
18
class S(BaseHTTPRequestHandler):
18
class S(BaseHTTPRequestHandler):
19
    def _output(self, code, content):
19
    def _output(self, code, content):
20
        self.send_response(code)
20
        self.send_response(code)
21
        self.send_header('Content-type', 'text/html')
21
        self.send_header('Content-type', 'text/html')
22
        self.end_headers()
22
        self.end_headers()
23
        self.wfile.write(content)
23
        self.wfile.write(content)
24
 
24
 
25
    def do_GET(self):
25
    def do_GET(self):
26
        try:
26
        try:
27
                action = parse_qs(self.path[2:]).get("action")[0]
27
                action = parse_qs(self.path[2:]).get("action")[0]
28
        except:
28
        except:
29
                action = None
29
                action = None
30
 
30
 
31
        output = '''<!DOCTYPE html>
31
        output = '''<!DOCTYPE html>
32
<html lang="en">
32
<html lang="en">
33
 
33
 
34
<head>
34
<head>
35
        <meta charset="UTF-8">
35
        <meta charset="UTF-8">
36
        <title>Motion camera</title>
36
        <title>Motion camera</title>
37
</head>
37
</head>
38
 
38
 
39
<body onload="onload()">
39
<body onload="onload()">
40
 
40
 
41
<script>
41
<script>
42
 
42
 
43
function sleep (time) {
43
function sleep (time) {
44
        return new Promise((resolve) => setTimeout(resolve, time));
44
        return new Promise((resolve) => setTimeout(resolve, time));
45
}
45
}
46
 
46
 
47
function _toggle_alarm(st) {
47
function _toggle_alarm(st) {
48
        document.getElementById("pleasewait").innerHTML = ' <i>Please wait...</i>';
48
        document.getElementById("pleasewait").innerHTML = ' <i>Please wait...</i>';
49
        var xhr = new XMLHttpRequest();
49
        var xhr = new XMLHttpRequest();
50
        xhr.onreadystatechange = function () {
50
        xhr.onreadystatechange = function () {
51
                var DONE = 4; // readyState 4 means the request is done.
51
                var DONE = 4; // readyState 4 means the request is done.
52
                var OK = 200; // status 200 is a successful return.
52
                var OK = 200; // status 200 is a successful return.
53
                if (xhr.readyState === DONE) {
53
                if (xhr.readyState === DONE) {
54
                        if (xhr.status === OK) {
54
                        if (xhr.status === OK) {
55
                                sleep(5000).then(() => {
55
                                sleep(5000).then(() => {
56
                                        document.location.reload();
56
                                        document.location.reload();
57
                                });
57
                                });
58
                        } else {
58
                        } else {
59
                                alert('Error: ' + xhr.status); // An error occurred during the request.
59
                                alert('Error: ' + xhr.status); // An error occurred during the request.
60
                        }
60
                        }
61
                }
61
                }
62
        };
62
        };
63
 
63
 
64
        var data = new FormData();
64
        var data = new FormData();
65
        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*/);
65
        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*/);
66
        xhr.open('POST', document.location, true);
66
        xhr.open('POST', document.location, true);
67
        xhr.send(data);
67
        xhr.send(data);
68
}
68
}
69
 
69
 
70
function onload() {
70
function onload() {
71
        if (document.getElementById('campic') != null) {
71
        if (document.getElementById('campic') != null) {
72
                document.getElementById('campic').src = 'http://' + window.location.hostname + ':'''+str(config.motion_stream_port)+'''/';
72
                document.getElementById('campic').src = 'http://' + window.location.hostname + ':'''+str(config.motion_stream_port)+'''/';
73
        }
73
        }
74
}
74
}
75
 
75
 
76
</script>'''
76
</script>'''
77
 
77
 
78
        if ismotionrunning():
78
        if ismotionrunning():
79
                output = output + '<h2>Motion detection ON</h2>'
79
                output = output + '<h2>Motion detection ON</h2>'
80
                output = output + '<p><a href="javascript:_toggle_alarm(0)">Disable motion detection</a><span id="pleasewait"></span></p>'
80
                output = output + '<p><a href="javascript:_toggle_alarm(0)">Disable motion detection</a><span id="pleasewait"></span></p>'
81
                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)
81
                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)
82
                output = output + '<p><img id="campic" src="" alt=""></p>';
82
                output = output + '<p><img id="campic" src="" alt=""></p>';
83
        else:
83
        else:
84
                output = output + '<h2>Motion detection OFF</h2>'
84
                output = output + '<h2>Motion detection OFF</h2>'
85
                output = output + '<p><a href="javascript:_toggle_alarm(1)">Enable motion detection</a><span id="pleasewait"></span></p>'
85
                output = output + '<p><a href="javascript:_toggle_alarm(1)">Enable motion detection</a><span id="pleasewait"></span></p>'
86
 
86
 
87
        output = output + '<h2>Subscribers</h2>'
87
        output = output + '<h2>Subscribers</h2>'
88
 
88
 
89
        found_subs = 0
89
        found_subs = 0
90
        for subscriber in g_subscribed[:]:
90
        for subscriber in g_subscribed[:]:
91
                if int(time.time()) > subscriber[2]:
91
                if int(time.time()) > subscriber[2]:
92
                        g_subscribed.remove(subscriber)
92
                        g_subscribed.remove(subscriber)
93
                else:
93
                else:
94
                        found_subs = found_subs + 1
94
                        found_subs = found_subs + 1
95
                        output = output + "<p>{0}:{1}</p>".format(subscriber[0], subscriber[1])
95
                        output = output + "<p>{0}:{1}</p>".format(subscriber[0], subscriber[1])
96
 
96
 
97
        if found_subs == 0:
97
        if found_subs == 0:
98
                output = output + '<p>None</p>'
98
                output = output + '<p>None</p>'
99
 
99
 
100
        output = output + '</body>'
100
        output = output + '</body>'
101
        output = output + '</html>'
101
        output = output + '</html>'
102
 
102
 
103
        self._output(200, output)
103
        self._output(200, output)
104
 
104
 
105
    def do_HEAD(self):
105
    def do_HEAD(self):
106
        self._output(200, '')
106
        self._output(200, '')
107
 
107
 
108
    def thr_client_notify(self, client_ip, client_port, server_targets):
108
    def thr_client_notify(self, client_ip, client_port, server_targets):
109
                print "ALERT: Will alert client http://{0}:{1} and tell that targets {2} sent an alert".format(client_ip, client_port, server_targets)
109
                print "ALERT: Will alert client http://{0}:{1} and tell that targets {2} sent an alert".format(client_ip, client_port, server_targets)
110
                d = {"action": "client_alert", # 1.3.6.1.4.1.37476.2.4.1.3
110
                d = {"action": "client_alert", # 1.3.6.1.4.1.37476.2.4.1.3
111
                     "targets": server_targets,
111
                     "targets": server_targets,
112
                     "motion_port": config.motion_stream_port,
112
                     "motion_port": config.motion_stream_port,
113
                     "simulation": "0"}
113
                     "simulation": "0"}
114
                requests.post("http://{0}:{1}".format(client_ip, client_port), data=d)
114
                requests.post("http://{0}:{1}".format(client_ip, client_port), data=d)
115
 
115
 
116
    def do_POST(self):
116
    def do_POST(self):
117
        # https://stackoverflow.com/questions/4233218/python-how-do-i-get-key-value-pairs-from-the-basehttprequesthandler-http-post-h
117
        # https://stackoverflow.com/questions/4233218/python-how-do-i-get-key-value-pairs-from-the-basehttprequesthandler-http-post-h
118
        # Question: Do we need the cgi package, or can we use functions available in this class (e.g. self_parse_qs?)
118
        # Question: Do we need the cgi package, or can we use functions available in this class (e.g. self_parse_qs?)
119
        ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
119
        ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
120
        if ctype == 'multipart/form-data':
120
        if ctype == 'multipart/form-data':
121
                postvars = cgi.parse_multipart(self.rfile, pdict)
121
                postvars = cgi.parse_multipart(self.rfile, pdict)
122
        elif ctype == 'application/x-www-form-urlencoded':
122
        elif ctype == 'application/x-www-form-urlencoded':
123
                length = int(self.headers.getheader('content-length'))
123
                length = int(self.headers.getheader('content-length'))
124
                postvars = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1)
124
                postvars = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1)
125
        else:
125
        else:
126
                postvars = {}
126
                postvars = {}
127
 
127
 
128
        # ---
128
        # ---
129
 
129
 
130
        global g_subscribed
130
        global g_subscribed
131
        global g_bellListenerProc
131
        global g_bellListenerProc
132
 
132
 
133
        if pvget(postvars, "action")[0] == "client_subscribe": # 1.3.6.1.4.1.37476.2.4.1.1
133
        if pvget(postvars, "action")[0] == "client_subscribe": # 1.3.6.1.4.1.37476.2.4.1.1
134
                client_ip      = self.client_address[0]
134
                client_ip      = self.client_address[0]
135
                client_port    = pvget(postvars, "port")[0]
135
                client_port    = pvget(postvars, "port")[0]
136
                client_ttl     = pvget(postvars, "ttl")[0]
136
                client_ttl     = pvget(postvars, "ttl")[0]
137
                client_targets = pvget(postvars, "targets")
137
                client_targets = pvget(postvars, "targets")
138
 
138
 
139
                client_expires = int(time.time()) + int(client_ttl)
139
                client_expires = int(time.time()) + int(client_ttl)
140
 
140
 
141
                print "Client subscribed: {0}:{1}, searching for targets {2}".format(client_ip, client_port, client_targets)
141
                print "Client subscribed: {0}:{1}, searching for targets {2}".format(client_ip, client_port, client_targets)
142
 
142
 
143
                # Remove all expired entries, and previous entries of that client
143
                # Remove all expired entries, and previous entries of that client
144
                for subscriber in g_subscribed[:]:
144
                for subscriber in g_subscribed[:]:
145
                        if int(time.time()) > subscriber[2]:
145
                        if int(time.time()) > subscriber[2]:
146
                                g_subscribed.remove(subscriber)
146
                                g_subscribed.remove(subscriber)
147
                        elif (subscriber[0] == client_ip) and (subscriber[1] == client_port) and (subscriber[3] == client_targets):
147
                        elif (subscriber[0] == client_ip) and (subscriber[1] == client_port) and (subscriber[3] == client_targets):
148
                                g_subscribed.remove(subscriber)
148
                                g_subscribed.remove(subscriber)
149
 
149
 
150
                # Now add our new client
150
                # Now add our new client
151
                g_subscribed.append([client_ip, client_port, client_expires, client_targets])
151
                g_subscribed.append([client_ip, client_port, client_expires, client_targets])
152
 
152
 
153
                # Send parameters of the device(s)
153
                # Send parameters of the device(s)
154
                d = {"action": "client_alert", # 1.3.6.1.4.1.37476.2.4.1.3
154
                d = {"action": "client_alert", # 1.3.6.1.4.1.37476.2.4.1.3
155
                     "targets": ['1.3.6.1.4.1.37476.2.4.2.0', '1.3.6.1.4.1.37476.2.4.2.1002'],
155
                     "targets": ['1.3.6.1.4.1.37476.2.4.2.0', '1.3.6.1.4.1.37476.2.4.2.1002'],
156
                     "motion_port": config.motion_stream_port,
156
                     "motion_port": config.motion_stream_port,
157
                     "simulation": "1"}
157
                     "simulation": "1"}
158
                requests.post("http://{0}:{1}".format(client_ip, client_port), data=d)
158
                requests.post("http://{0}:{1}".format(client_ip, client_port), data=d)
159
 
159
 
160
        if pvget(postvars, "action")[0] == "server_alert": # 1.3.6.1.4.1.37476.2.4.1.2
160
        if pvget(postvars, "action")[0] == "server_alert": # 1.3.6.1.4.1.37476.2.4.1.2
161
                server_targets = pvget(postvars, "targets")
161
                server_targets = pvget(postvars, "targets")
162
 
162
 
163
                found_g = 0
163
                found_g = 0
164
 
164
 
165
                for subscriber in g_subscribed[:]:
165
                for subscriber in g_subscribed[:]:
166
                        client_ip      = subscriber[0]
166
                        client_ip      = subscriber[0]
167
                        client_port    = subscriber[1]
167
                        client_port    = subscriber[1]
168
                        client_expires = subscriber[2]
168
                        client_expires = subscriber[2]
169
                        client_targets = subscriber[3]
169
                        client_targets = subscriber[3]
170
 
170
 
171
                        if int(time.time()) > client_expires:
171
                        if int(time.time()) > client_expires:
172
                                g_subscribed.remove(subscriber)
172
                                g_subscribed.remove(subscriber)
173
                        else:
173
                        else:
174
                                found_c = 0
174
                                found_c = 0
175
                                for st in server_targets:
175
                                for st in server_targets:
176
                                        for ct in client_targets:
176
                                        for ct in client_targets:
177
                                                if ct == st:
177
                                                if ct == st:
178
                                                        found_c = found_c + 1
178
                                                        found_c = found_c + 1
179
                                                        found_g = found_g + 1
179
                                                        found_g = found_g + 1
180
                                if found_c > 0:
180
                                if found_c > 0:
181
                                        # Notify clients via threads, so that all clients are equally fast notified
181
                                        # Notify clients via threads, so that all clients are equally fast notified
182
                                        thread = threading.Thread(target=self.thr_client_notify, args=(client_ip, client_port, server_targets, ))
182
                                        thread = threading.Thread(target=self.thr_client_notify, args=(client_ip, client_port, server_targets, ))
183
                                        thread.start()
183
                                        thread.start()
184
 
184
 
185
                if found_g == 0:
185
                if found_g == 0:
186
                        print "ALERT {0}, but nobody is listening!".format(server_targets)
186
                        print "ALERT {0}, but nobody is listening!".format(server_targets)
187
 
187
 
188
        if pvget(postvars, "action")[0] == "motion_on": # 1.3.6.1.4.1.37476.2.4.1.100
188
        if pvget(postvars, "action")[0] == "motion_on": # 1.3.6.1.4.1.37476.2.4.1.100
189
                # TODO: Actually, we should call this API "alarm_on" instead of "motion_on", since we also enable a doorbell checker
189
                # TODO: Actually, we should call this API "alarm_on" instead of "motion_on", since we also enable a doorbell checker
190
                print "Motion start"
190
                print "Motion start"
191
                if config.enable_motion_detect:
191
                if config.enable_motion_detect:
192
                        os.system(os.path.dirname(os.path.abspath(__file__)) + "/motion/motion_start_safe")
192
                        os.system(os.path.dirname(os.path.abspath(__file__)) + "/motion/motion_start_safe")
193
                if config.enable_doorbell_listener:
193
                if config.enable_doorbell_listener:
194
                        g_bellListenerProc = sp.Popen([os.path.dirname(os.path.abspath(__file__)) + "/doorbell/bell_listener.py"])
194
                        g_bellListenerProc = sp.Popen([os.path.dirname(os.path.abspath(__file__)) + "/doorbell/bell_listener.py"])
195
        if pvget(postvars, "action")[0] == "motion_off": # 1.3.6.1.4.1.37476.2.4.1.101
195
        if pvget(postvars, "action")[0] == "motion_off": # 1.3.6.1.4.1.37476.2.4.1.101
196
                # TODO: Actually, we should call this API "alarm_off" instead of "motion_off", since we also disable a doorbell checker
196
                # TODO: Actually, we should call this API "alarm_off" instead of "motion_off", since we also disable a doorbell checker
197
                print "Motion stop"
197
                print "Motion stop"
198
                if config.enable_motion_detect:
198
                if config.enable_motion_detect:
199
                        os.system(os.path.dirname(os.path.abspath(__file__)) + "/motion/motion_stop_safe")
199
                        os.system(os.path.dirname(os.path.abspath(__file__)) + "/motion/motion_stop_safe")
200
                if config.enable_doorbell_listener:
200
                if config.enable_doorbell_listener:
-
 
201
                        if g_bellListenerProc != None:
201
                        sp.Popen.terminate(g_bellListenerProc)
202
                                sp.Popen.terminate(g_bellListenerProc)
202
 
203
 
203
        self._output(200, '')
204
        self._output(200, '')
204
 
205
 
205
def pvget(ary, key):
206
def pvget(ary, key):
206
        if ary.get(key) == None:
207
        if ary.get(key) == None:
207
                return [""]
208
                return [""]
208
        else:
209
        else:
209
                return ary.get(key)
210
                return ary.get(key)
210
 
211
 
211
def run(server_class=HTTPServer, handler_class=S, port=8085):
212
def run(server_class=HTTPServer, handler_class=S, port=8085):
212
        print 'Starting server, listening to port {0}...'.format(port)
213
        print 'Starting server, listening to port {0}...'.format(port)
213
        server_address = ('', port)
214
        server_address = ('', port)
214
        httpd = server_class(server_address, handler_class)
215
        httpd = server_class(server_address, handler_class)
215
        httpd.serve_forever()
216
        httpd.serve_forever()
216
 
217
 
217
def ismotionrunning():
218
def ismotionrunning():
218
        p = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
219
        p = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
219
        out, err = p.communicate()
220
        out, err = p.communicate()
220
        try:
221
        try:
221
                return ('/usr/bin/motion' in str(out))
222
                return ('/usr/bin/motion' in str(out))
222
        except:
223
        except:
223
                res = os.system("pidof -x /usr/bin/motion > /dev/null")
224
                res = os.system("pidof -x /usr/bin/motion > /dev/null")
224
                return (res >> 8) == 0
225
                return (res >> 8) == 0
225
 
226
 
226
if __name__ == "__main__":
227
if __name__ == "__main__":
227
        from sys import argv
228
        from sys import argv
228
 
229
 
229
        if len(argv) == 2:
230
        if len(argv) == 2:
230
                run(port=int(argv[1]))
231
                run(port=int(argv[1]))
231
        else:
232
        else:
232
                run()
233
                run()
233
 
234
 
234
 
235