Subversion Repositories alarming

Rev

Rev 6 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 6 Rev 8
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
                print "Motion start"
190
                print "Motion start"
190
                if config.enable_motion_detect:
191
                if config.enable_motion_detect:
191
                        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")
192
                if config.enable_doorbell_listener:
193
                if config.enable_doorbell_listener:
193
                        g_bellListenerProc = sp.Popen(['python3',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"])
194
        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
195
                print "Motion stop"
197
                print "Motion stop"
196
                if config.enable_motion_detect:
198
                if config.enable_motion_detect:
197
                        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")
198
                if config.enable_doorbell_listener:
200
                if config.enable_doorbell_listener:
199
                        sp.Popen.terminate(g_bellListenerProc)
201
                        sp.Popen.terminate(g_bellListenerProc)
200
 
202
 
201
        self._output(200, '')
203
        self._output(200, '')
202
 
204
 
203
def pvget(ary, key):
205
def pvget(ary, key):
204
        if ary.get(key) == None:
206
        if ary.get(key) == None:
205
                return [""]
207
                return [""]
206
        else:
208
        else:
207
                return ary.get(key)
209
                return ary.get(key)
208
 
210
 
209
def run(server_class=HTTPServer, handler_class=S, port=8085):
211
def run(server_class=HTTPServer, handler_class=S, port=8085):
210
        print 'Starting server, listening to port {0}...'.format(port)
212
        print 'Starting server, listening to port {0}...'.format(port)
211
        server_address = ('', port)
213
        server_address = ('', port)
212
        httpd = server_class(server_address, handler_class)
214
        httpd = server_class(server_address, handler_class)
213
        httpd.serve_forever()
215
        httpd.serve_forever()
214
 
216
 
215
def ismotionrunning():
217
def ismotionrunning():
216
        p = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
218
        p = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
217
        out, err = p.communicate()
219
        out, err = p.communicate()
218
        try:
220
        try:
219
                return ('/usr/bin/motion' in str(out))
221
                return ('/usr/bin/motion' in str(out))
220
        except:
222
        except:
221
                res = os.system("pidof -x /usr/bin/motion > /dev/null")
223
                res = os.system("pidof -x /usr/bin/motion > /dev/null")
222
                return (res >> 8) == 0
224
                return (res >> 8) == 0
223
 
225
 
224
if __name__ == "__main__":
226
if __name__ == "__main__":
225
        from sys import argv
227
        from sys import argv
226
 
228
 
227
        if len(argv) == 2:
229
        if len(argv) == 2:
228
                run(port=int(argv[1]))
230
                run(port=int(argv[1]))
229
        else:
231
        else:
230
                run()
232
                run()
231
 
233
 
232
 
234