-
Notifications
You must be signed in to change notification settings - Fork 67
/
alarmserver.py
executable file
·652 lines (555 loc) · 27.2 KB
/
alarmserver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
#!/usr/bin/python
## Alarm Server
## Supporting Envisalink 2DS/3
## Written by donnyk+envisalink@gmail.com
## Lightly improved by leaberry@gmail.com
##
## This code is under the terms of the GPL v3 license.
import asyncore, asynchat
import ConfigParser
import datetime
import os, socket, string, sys, httplib, urllib, urlparse, ssl
import StringIO, mimetools
import json
import hashlib
import time
import getopt
import requests
from envisalinkdefs import evl_ResponseTypes
from envisalinkdefs import evl_Defaults
from envisalinkdefs import evl_ArmModes
LOGTOFILE = False
class CodeError(Exception): pass
ALARMSTATE={'version' : 0.1}
MAXPARTITIONS=16
MAXZONES=128
MAXALARMUSERS=47
CONNECTEDCLIENTS={}
def dict_merge(a, b):
c = a.copy()
c.update(b)
return c
def getMessageType(code):
return evl_ResponseTypes[code]
def alarmserver_logger(message, type = 0, level = 0):
if LOGTOFILE:
outfile.write(str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))+' '+message+'\n')
outfile.flush()
else:
print (str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))+' '+message)
def to_chars(string):
chars = []
for char in string:
chars.append(ord(char))
return chars
def get_checksum(code, data):
return ("%02X" % sum(to_chars(code)+to_chars(data)))[-2:]
#currently supports pushover notifications, more to be added
#including email, text, etc.
#to be fixed!
def send_notification(config, message):
if config.PUSHOVER_ENABLE == True:
conn = httplib.HTTPSConnection("api.pushover.net:443")
conn.request("POST", "/1/messages.json",
urllib.urlencode({
"token": "qo0nwMNdX56KJl0Avd4NHE2onO4Xff",
"user": config.PUSHOVER_USERTOKEN,
"message": str(message),
}), { "Content-type": "application/x-www-form-urlencoded" })
class AlarmServerConfig():
def __init__(self, configfile):
self._config = ConfigParser.ConfigParser()
self._config.read(configfile)
self.LOGURLREQUESTS = self.read_config_var('alarmserver', 'logurlrequests', True, 'bool')
self.HTTPSPORT = self.read_config_var('alarmserver', 'httpsport', 8111, 'int')
self.CERTFILE = self.read_config_var('alarmserver', 'certfile', 'server.crt', 'str')
self.KEYFILE = self.read_config_var('alarmserver', 'keyfile', 'server.key', 'str')
self.MAXEVENTS = self.read_config_var('alarmserver', 'maxevents', 10, 'int')
self.MAXALLEVENTS = self.read_config_var('alarmserver', 'maxallevents', 100, 'int')
self.ENVISALINKHOST = self.read_config_var('envisalink', 'host', 'envisalink', 'str')
self.ENVISALINKPORT = self.read_config_var('envisalink', 'port', 4025, 'int')
self.ENVISALINKPASS = self.read_config_var('envisalink', 'pass', 'user', 'str')
self.ENABLEPROXY = self.read_config_var('envisalink', 'enableproxy', True, 'bool')
self.ENVISALINKPROXYPORT = self.read_config_var('envisalink', 'proxyport', self.ENVISALINKPORT, 'int')
self.ENVISALINKPROXYPASS = self.read_config_var('envisalink', 'proxypass', self.ENVISALINKPASS, 'str')
self.PUSHOVER_ENABLE = self.read_config_var('pushover', 'enable', False, 'bool')
self.PUSHOVER_USERTOKEN = self.read_config_var('pushover', 'enable', False, 'bool')
self.ALARMCODE = self.read_config_var('envisalink', 'alarmcode', 1111, 'int')
self.EVENTTIMEAGO = self.read_config_var('alarmserver', 'eventtimeago', True, 'bool')
self.LOGFILE = self.read_config_var('alarmserver', 'logfile', '', 'str')
self.CALLBACKURL_BASE = self.read_config_var('alarmserver', 'callbackurl_base', '', 'str')
self.CALLBACKURL_APP_ID = self.read_config_var('alarmserver', 'callbackurl_app_id', '', 'str')
self.CALLBACKURL_ACCESS_TOKEN = self.read_config_var('alarmserver', 'callbackurl_access_token', '', 'str')
self.CALLBACKURL_EVENT_CODES = self.read_config_var('alarmserver', 'callbackurl_event_codes', '', 'str')
global LOGTOFILE
if self.LOGFILE == '':
LOGTOFILE = False
else:
LOGTOFILE = True
self.PARTITIONNAMES={}
for i in range(1, MAXPARTITIONS+1):
self.PARTITIONNAMES[i]=self.read_config_var('alarmserver', 'partition'+str(i), False, 'str', True)
self.ZONENAMES={}
for i in range(1, MAXZONES+1):
self.ZONENAMES[i]=self.read_config_var('alarmserver', 'zone'+str(i), False, 'str', True)
self.ALARMUSERNAMES={}
for i in range(1, MAXALARMUSERS+1):
self.ALARMUSERNAMES[i]=self.read_config_var('alarmserver', 'user'+str(i), False, 'str', True)
if self.PUSHOVER_USERTOKEN == False and self.PUSHOVER_ENABLE == True: self.PUSHOVER_ENABLE = False
def defaulting(self, section, variable, default, quiet = False):
if quiet == False:
print('Config option '+ str(variable) + ' not set in ['+str(section)+'] defaulting to: \''+str(default)+'\'')
def read_config_var(self, section, variable, default, type = 'str', quiet = False):
try:
if type == 'str':
return self._config.get(section,variable)
elif type == 'bool':
return self._config.getboolean(section,variable)
elif type == 'int':
return int(self._config.get(section,variable))
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
self.defaulting(section, variable, default, quiet)
return default
class HTTPChannel(asynchat.async_chat):
def __init__(self, server, sock, addr):
asynchat.async_chat.__init__(self, sock)
self.server = server
self.set_terminator("\r\n\r\n")
self.header = None
self.data = ""
self.shutdown = 0
def collect_incoming_data(self, data):
self.data = self.data + data
if len(self.data) > 16384:
# limit the header size to prevent attacks
self.shutdown = 1
def found_terminator(self):
if not self.header:
# parse http header
fp = StringIO.StringIO(self.data)
request = string.split(fp.readline(), None, 2)
if len(request) != 3:
# badly formed request; just shut down
self.shutdown = 1
else:
# parse message header
self.header = mimetools.Message(fp)
self.set_terminator("\r\n")
self.server.handle_request(
self, request[0], request[1], self.header
)
self.close_when_done()
self.data = ""
else:
pass # ignore body data, for now
def pushstatus(self, status, explanation="OK"):
self.push("HTTP/1.0 %d %s\r\n" % (status, explanation))
def pushok(self, content):
self.pushstatus(200, "OK")
self.push('Content-type: application/json\r\n')
self.push('Expires: Sat, 26 Jul 1997 05:00:00 GMT\r\n')
self.push('Last-Modified: '+ datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")+' GMT\r\n')
self.push('Cache-Control: no-store, no-cache, must-revalidate\r\n' )
self.push('Cache-Control: post-check=0, pre-check=0\r\n')
self.push('Pragma: no-cache\r\n' )
self.push('\r\n')
self.push(content)
def pushfile(self, file):
self.pushstatus(200, "OK")
extension = os.path.splitext(file)[1]
if extension == ".html":
self.push("Content-type: text/html\r\n")
elif extension == ".js":
self.push("Content-type: text/javascript\r\n")
elif extension == ".png":
self.push("Content-type: image/png\r\n")
elif extension == ".css":
self.push("Content-type: text/css\r\n")
self.push("\r\n")
self.push_with_producer(push_FileProducer(sys.path[0] + os.sep + 'ext' + os.sep + file))
class EnvisalinkClient(asynchat.async_chat):
def __init__(self, config):
# Call parent class's __init__ method
asynchat.async_chat.__init__(self)
# Define some private instance variables
self._buffer = []
# Are we logged in?
self._loggedin = False
# Set our terminator to \n
self.set_terminator("\r\n")
# Set config
self._config = config
# Reconnect delay
self._retrydelay = 10
self.do_connect()
def do_connect(self, reconnect = False):
# Create the socket and connect to the server
if reconnect == True:
alarmserver_logger('Connection failed, retrying in '+str(self._retrydelay)+ ' seconds')
for i in range(0, self._retrydelay):
time.sleep(1)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((self._config.ENVISALINKHOST, self._config.ENVISALINKPORT))
def collect_incoming_data(self, data):
# Append incoming data to the buffer
self._buffer.append(data)
def found_terminator(self):
line = "".join(self._buffer)
self.handle_line(line)
self._buffer = []
def handle_connect(self):
alarmserver_logger("Connected to %s:%i" % (self._config.ENVISALINKHOST, self._config.ENVISALINKPORT))
def handle_close(self):
self._loggedin = False
self.close()
alarmserver_logger("Disconnected from %s:%i" % (self._config.ENVISALINKHOST, self._config.ENVISALINKPORT))
self.do_connect(True)
def handle_eerror(self):
self._loggedin = False
self.close()
alarmserver_logger("Error, disconnected from %s:%i" % (self._config.ENVISALINKHOST, self._config.ENVISALINKPORT))
self.do_connect(True)
def send_command(self, code, data, checksum = True):
if checksum == True:
to_send = code+data+get_checksum(code,data)+'\r\n'
else:
to_send = code+data+'\r\n'
alarmserver_logger('TX > '+to_send[:-1])
self.push(to_send)
def handle_line(self, input):
if input != '':
for client in CONNECTEDCLIENTS:
CONNECTEDCLIENTS[client].send_command(input, False)
code=int(input[:3])
parameters=input[3:][:-2]
event = getMessageType(int(code))
message = self.format_event(event, parameters)
alarmserver_logger('RX < ' +str(code)+' - '+message)
try:
handler = "handle_%s" % evl_ResponseTypes[code]['handler']
except KeyError:
#call general event handler
self.handle_event(code, parameters, event, message)
return
try:
func = getattr(self, handler)
except AttributeError:
raise CodeError("Handler function doesn't exist")
func(code, parameters, event, message)
def format_event(self, event, parameters):
if 'type' in event:
if event['type'] in ('partition', 'zone'):
if event['type'] == 'partition':
# If parameters includes extra digits then this next line would fail
# without looking at just the first digit which is the partition number
if int(parameters[0]) in self._config.PARTITIONNAMES:
if self._config.PARTITIONNAMES[int(parameters[0])]!=False:
# After partition number can be either a usercode
# or for event 652 a type of arm mode (single digit)
# Usercode is always 4 digits padded with zeros
if len(str(parameters)) == 5:
# We have a usercode
try:
usercode = int(parameters[1:5])
except:
usercode = 0
if int(usercode) in self._config.ALARMUSERNAMES:
if self._config.ALARMUSERNAMES[int(usercode)]!=False:
alarmusername = self._config.ALARMUSERNAMES[int(usercode)]
else:
# Didn't find a username, use the code instead
alarmusername = usercode
return event['name'].format(str(self._config.PARTITIONNAMES[int(parameters[0])]), str(alarmusername))
elif len(parameters) == 2:
# We have an arm mode instead, get it's friendly name
armmode = evl_ArmModes[int(parameters[1])]
return event['name'].format(str(self._config.PARTITIONNAMES[int(parameters[0])]), str(armmode))
else:
return event['name'].format(str(self._config.PARTITIONNAMES[int(parameters)]))
elif event['type'] == 'zone':
if int(parameters) in self._config.ZONENAMES:
if self._config.ZONENAMES[int(parameters)]!=False:
return event['name'].format(str(self._config.ZONENAMES[int(parameters)]))
return event['name'].format(str(parameters))
#envisalink event handlers, some events are unhandeled.
def handle_login(self, code, parameters, event, message):
if parameters == '3':
self._loggedin = True
self.send_command('005', self._config.ENVISALINKPASS)
if parameters == '1':
self.send_command('001', '')
if parameters == '0':
alarmserver_logger('Incorrect envisalink password')
sys.exit(0)
def handle_event(self, code, parameters, event, message):
if 'type' in event:
if not event['type'] in ALARMSTATE: ALARMSTATE[event['type']]={'lastevents' : []}
if event['type'] in ('partition', 'zone'):
if event['type'] == 'zone':
if int(parameters) in self._config.ZONENAMES:
if not int(parameters) in ALARMSTATE[event['type']]:
ALARMSTATE[event['type']][int(parameters)] = {'name' : self._config.ZONENAMES[int(parameters)]}
else:
if not int(parameters) in ALARMSTATE[event['type']]: ALARMSTATE[event['type']][int(parameters)] = {}
elif event['type'] == 'partition':
if int(parameters) in self._config.PARTITIONNAMES:
if not int(parameters) in ALARMSTATE[event['type']]: ALARMSTATE[event['type']][int(parameters)] = {'name' : self._config.PARTITIONNAMES[int(parameters)]}
else:
if not int(parameters) in ALARMSTATE[event['type']]: ALARMSTATE[event['type']][int(parameters)] = {}
else:
if not int(parameters) in ALARMSTATE[event['type']]: ALARMSTATE[event['type']][int(parameters)] = {}
if not 'lastevents' in ALARMSTATE[event['type']][int(parameters)]: ALARMSTATE[event['type']][int(parameters)]['lastevents'] = []
if not 'status' in ALARMSTATE[event['type']][int(parameters)]:
if not 'type' in event:
ALARMSTATE[event['type']][int(parameters)]['status'] = {}
else:
ALARMSTATE[event['type']][int(parameters)]['status'] = evl_Defaults[event['type']]
if 'status' in event:
ALARMSTATE[event['type']][int(parameters)]['status']=dict_merge(ALARMSTATE[event['type']][int(parameters)]['status'], event['status'])
if len(ALARMSTATE[event['type']][int(parameters)]['lastevents']) > self._config.MAXEVENTS:
ALARMSTATE[event['type']][int(parameters)]['lastevents'].pop(0)
ALARMSTATE[event['type']][int(parameters)]['lastevents'].append({'datetime' : str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")), 'message' : message})
if len(ALARMSTATE[event['type']]['lastevents']) > self._config.MAXALLEVENTS:
ALARMSTATE[event['type']]['lastevents'].pop(0)
ALARMSTATE[event['type']]['lastevents'].append({'datetime' : str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")), 'message' : message})
self.callbackurl_event(code, parameters, event, message)
def handle_zone(self, code, parameters, event, message):
self.handle_event(code, parameters[1:], event, message)
def handle_partition(self, code, parameters, event, message):
self.handle_event(code, parameters[0], event, message)
def callbackurl_event(self, code, parameters, event, message):
myEvents = self._config.CALLBACKURL_EVENT_CODES.split(',')
# Determin what events we are sending to smartthings then send if we match
if str(code) in myEvents:
# Now check if Zone has a custom name, if it does then send notice to Smartthings
# Check for event type
if event['type'] == 'partition':
# Is our partition setup with a custom name?
if int(parameters[0]) in self._config.PARTITIONNAMES:
myURL = self._config.CALLBACKURL_BASE + "/" + self._config.CALLBACKURL_APP_ID + "/panel/" + str(code) + "/" + str(int(parameters[0])) + "?access_token=" + self._config.CALLBACKURL_ACCESS_TOKEN
else:
# We don't care about this partition
return
elif event['type'] == 'zone':
# Is our zone setup with a custom name, if so we care about it
if self._config.ZONENAMES[int(parameters)]:
myURL = self._config.CALLBACKURL_BASE + "/" + self._config.CALLBACKURL_APP_ID + "/panel/" + str(code) + "/" + str(int(parameters)) + "?access_token=" + self._config.CALLBACKURL_ACCESS_TOKEN
else:
# We don't care about this zone
return
else:
# Unhandled event type..
return
# If we made it here we should send to Smartthings
try:
# Note: I don't currently care about the return value, fire and forget right now
requests.get(myURL)
#print "myURL: ", myURL
#print "Exit code: ", r.status_code
#print "Response data: ", r.text
time.sleep(0.5)
except:
print sys.exc_info()[0]
class push_FileProducer:
# a producer which reads data from a file object
def __init__(self, file):
self.file = open(file, "rb")
def more(self):
if self.file:
data = self.file.read(2048)
if data:
return data
self.file = None
return ""
class AlarmServer(asyncore.dispatcher):
def __init__(self, config):
# Call parent class's __init__ method
asyncore.dispatcher.__init__(self)
# Create Envisalink client object
self._envisalinkclient = EnvisalinkClient(config)
#Store config
self._config = config
# Create socket and listen on it
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.bind(("", config.HTTPSPORT))
self.listen(5)
def handle_accept(self):
# Accept the connection
conn, addr = self.accept()
if (config.LOGURLREQUESTS):
alarmserver_logger('Incoming web connection from %s' % repr(addr))
try:
# HTTPChannel(self, conn, addr)
HTTPChannel(self, ssl.wrap_socket(conn, server_side=True, certfile=config.CERTFILE, keyfile=config.KEYFILE, ssl_version=ssl.PROTOCOL_TLSv1), addr)
except ssl.SSLError:
return
def handle_request(self, channel, method, request, header):
if (config.LOGURLREQUESTS):
alarmserver_logger('Web request: '+str(method)+' '+str(request))
query = urlparse.urlparse(request)
query_array = urlparse.parse_qs(query.query, True)
if query.path == '/':
channel.pushfile('index.html');
elif query.path == '/api':
channel.pushok(json.dumps(ALARMSTATE))
elif query.path == '/api/alarm/arm':
channel.pushok(json.dumps({'response' : 'Request to arm received'}))
self._envisalinkclient.send_command('030', '1')
elif query.path == '/api/alarm/stayarm':
channel.pushok(json.dumps({'response' : 'Request to arm in stay received'}))
self._envisalinkclient.send_command('031', '1')
elif query.path == '/api/alarm/armwithcode':
channel.pushok(json.dumps({'response' : 'Request to arm with code received'}))
self._envisalinkclient.send_command('033', '1' + str(query_array['alarmcode'][0]))
elif query.path == '/api/pgm':
channel.pushok(json.dumps({'response' : 'Request to trigger PGM'}))
#self._envisalinkclient.send_command('020', '1' + str(query_array['pgmnum'][0]))
self._envisalinkclient.send_command('071', '1' + "*7" + str(query_array['pgmnum'][0]))
time.sleep(1)
self._envisalinkclient.send_command('071', '1' + str(query_array['alarmcode'][0]))
elif query.path == '/api/alarm/disarm':
channel.pushok(json.dumps({'response' : 'Request to disarm received'}))
if 'alarmcode' in query_array:
self._envisalinkclient.send_command('040', '1' + str(query_array['alarmcode'][0]))
else:
self._envisalinkclient.send_command('040', '1' + str(self._config.ALARMCODE))
elif query.path == '/api/refresh':
channel.pushok(json.dumps({'response' : 'Request to refresh data received'}))
self._envisalinkclient.send_command('001', '')
elif query.path == '/api/config/eventtimeago':
channel.pushok(json.dumps({'eventtimeago' : str(self._config.EVENTTIMEAGO)}))
elif query.path == '/img/glyphicons-halflings.png':
channel.pushfile('glyphicons-halflings.png')
elif query.path == '/img/glyphicons-halflings-white.png':
channel.pushfile('glyphicons-halflings-white.png')
elif query.path == '/favicon.ico':
channel.pushfile('favicon.ico')
else:
if len(query.path.split('/')) == 2:
try:
with open(sys.path[0] + os.sep + 'ext' + os.sep + query.path.split('/')[1]) as f:
f.close()
channel.pushfile(query.path.split('/')[1])
except IOError as e:
print "I/O error({0}): {1}".format(e.errno, e.strerror)
channel.pushstatus(404, "Not found")
channel.push("Content-type: text/html\r\n")
channel.push("File not found")
channel.push("\r\n")
else:
if (config.LOGURLREQUESTS):
alarmserver_logger("Invalid file requested")
channel.pushstatus(404, "Not found")
channel.push("Content-type: text/html\r\n")
channel.push("\r\n")
class ProxyChannel(asynchat.async_chat):
def __init__(self, server, proxypass, sock, addr):
asynchat.async_chat.__init__(self, sock)
self.server = server
self.set_terminator("\r\n")
self._buffer = []
self._server = server
self._clientMD5 = hashlib.md5(str(addr)).hexdigest()
self._straddr = str(addr)
self._proxypass = proxypass
self._authenticated = False
self.send_command('5053')
def collect_incoming_data(self, data):
# Append incoming data to the buffer
self._buffer.append(data)
def found_terminator(self):
line = "".join(self._buffer)
self._buffer = []
self.handle_line(line)
def handle_line(self, line):
alarmserver_logger('PROXY REQ < '+line)
if self._authenticated == True:
self._server._envisalinkclient.send_command(line, '', False)
else:
self.send_command('500005')
expectedstring = '005' + self._proxypass + get_checksum('005', self._proxypass)
if line == ('005' + self._proxypass + get_checksum('005', self._proxypass)):
alarmserver_logger('Proxy User Authenticated')
CONNECTEDCLIENTS[self._straddr]=self
self._authenticated = True
self.send_command('5051')
else:
alarmserver_logger('Proxy User Authentication failed')
self.send_command('5050')
self.close()
def send_command(self, data, checksum = True):
if checksum == True:
to_send = data+get_checksum(data, '')+'\r\n'
else:
to_send = data+'\r\n'
self.push(to_send)
def handle_close(self):
alarmserver_logger('Proxy connection from %s closed' % self._straddr)
if self._straddr in CONNECTEDCLIENTS: del CONNECTEDCLIENTS[self._straddr]
self.close()
def handle_error(self):
alarmserver_logger('Proxy connection from %s errored' % self._straddr)
if self._straddr in CONNECTEDCLIENTS: del CONNECTEDCLIENTS[self._straddr]
self.close()
class EnvisalinkProxy(asyncore.dispatcher):
def __init__(self, config, server):
self._config = config
if self._config.ENABLEPROXY == False:
return
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
alarmserver_logger('Envisalink Proxy Started')
self.bind(("", self._config.ENVISALINKPROXYPORT))
self.listen(5)
def handle_accept(self):
pair = self.accept()
if pair is None:
pass
else:
sock, addr = pair
alarmserver_logger('Incoming proxy connection from %s' % repr(addr))
handler = ProxyChannel(server, self._config.ENVISALINKPROXYPASS, sock, addr)
def usage():
print 'Usage: '+sys.argv[0]+' -c <configfile>'
def main(argv):
try:
opts, args = getopt.getopt(argv, "hc:", ["help", "config="])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-h", "--help"):
usage()
sys.exit()
elif opt in ("-c", "--config"):
global conffile
conffile = arg
if __name__=="__main__":
conffile='alarmserver.cfg'
main(sys.argv[1:])
print('Using configuration file %s' % conffile)
config = AlarmServerConfig(conffile)
if LOGTOFILE:
outfile=open(config.LOGFILE,'a')
print ('Writing logfile to %s' % config.LOGFILE)
alarmserver_logger('Alarm Server Starting')
alarmserver_logger('Currently Supporting Envisalink 2DS/3 only')
alarmserver_logger('Tested on a DSC-1616 + EVL-3')
alarmserver_logger('and on a DSC-1832 + EVL-2DS')
alarmserver_logger('and on a DSC-1864 v4.6 + EVL-3')
server = AlarmServer(config)
proxy = EnvisalinkProxy(config, server)
try:
while True:
asyncore.loop(timeout=2, count=1)
# insert scheduling code here.
except KeyboardInterrupt:
print "Crtl+C pressed. Shutting down."
alarmserver_logger('Shutting down from Ctrl+C')
if LOGTOFILE:
outfile.close()
server.shutdown(socket.SHUT_RDWR)
server.close()
sys.exit()