diff --git a/alarmserver-example.cfg b/alarmserver-example.cfg index d0d5dd1..32a9f69 100644 --- a/alarmserver-example.cfg +++ b/alarmserver-example.cfg @@ -9,7 +9,6 @@ logfile= ## seconds with the web ui's. To disable all these set this to False logurlrequests=True - ## The server runs with SSL. You need a certificate and key ## server.crt and server.key are included but you should ## generate your own. @@ -34,10 +33,12 @@ httpsport=8111 eventtimeago=True ## Name of your parition(s) +## Note: For callbackURL setup only defined zones and paritions are sent callback data partition1=Home ## Zone names. Delete the zones you're not using to have them hidden. ## Add more zoneXX if you need more zones +## Note: For callbackURL setup only defined zones and paritions are sent callback data zone1=A zone2=B zone3=C @@ -60,6 +61,21 @@ user1=MyUser1 user2=MyUser2 user3=MyUser3 +## Experimental CallbackURL functionality - Mainly for Smartthings right now +## The resulting path for callbackurls looks like this +## This likely needs to be fixed to allow more flexibility for any service? +## Suggestions welcome +## ${callbackurl_base}/${callbackurl_app_id}/panel/${code}/${zoneorpartitionnumber}?access_token=${callbackurl_access_token} + +## Smartthings or generic callback URL setup +callbackurl_base=https://graph.api.smartthings.com/api/smartapps/installations +callbackurl_app_id=your_app_id_from_smartthings_or_other_oAuth_setup +callbackurl_access_token=your_resulting_acces_token_after_auth_setup +## Define the event codes you want callbacks for, the codes below +## cover zone open/close, partition ready, not ready, armed, exit delay, entry delay and in alarm status +## these should cover most use cases +callbackurl_event_codes=601,602,609,610,650,651,652,654,656,657 + [pushover] enable=False usertoken=tokengoeshere diff --git a/alarmserver.py b/alarmserver.py index 2ed02ff..73bdca1 100755 --- a/alarmserver.py +++ b/alarmserver.py @@ -16,6 +16,7 @@ import hashlib import time import getopt +import requests from envisalinkdefs import evl_ResponseTypes from envisalinkdefs import evl_Defaults @@ -92,6 +93,10 @@ def __init__(self, configfile): 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 @@ -337,7 +342,8 @@ def handle_event(self, code, parameters, event, message): 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)]} + 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': @@ -365,6 +371,7 @@ def handle_event(self, code, parameters, event, 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) @@ -372,6 +379,41 @@ def handle_zone(self, code, parameters, 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 @@ -410,9 +452,9 @@ def handle_accept(self): 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: - alarmserver_logger('Failed https connection, attempted with http') return def handle_request(self, channel, method, request, header): @@ -607,4 +649,4 @@ def main(argv): server.shutdown(socket.SHUT_RDWR) server.close() - sys.exit() \ No newline at end of file + sys.exit()