diff --git a/README.md b/README.md index fa794af..732c90b 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ unless you are developer you can use the develop-Branch - may be unstable! ### Features ##### Implemented Features: -- FMS, ZVEI and POCSAG512/1200 decoding and Displaying +- FMS, ZVEI and POCSAG512/1200/2400 decoding and Displaying - Plugin support for easy Functions extension - Filtering double alarms with adjustable time -- Filtering of POCSAG RIC´s +- Filtering Range of POCSAG RIC´s - All configurations in seperate config File - Data validation (plausibility test) - Logfiles for better Troubleshooting @@ -24,16 +24,16 @@ unless you are developer you can use the develop-Branch - may be unstable! ##### Features for the Future: - extensive filtering options -- POCSAG 2400 support (need RAW data from multimon-ng) +- more Plugins ###Plugins ##### Implemented Plugins: -- MySQL -- BosMon +- MySQL (insert Data into MySQL Database [FMS|ZVEI|POC]) +- BosMon (send Data to BosMon Server [POC]) +- httpRequest (send a request to an URL [FMS|ZVEI|POC]) ##### Plugins for the Future: -- HTTP-Push - E-mail Notification - Other Ideas per Issues diff --git a/boswatch.py b/boswatch.py index 2b5c116..2cb2798 100644 --- a/boswatch.py +++ b/boswatch.py @@ -236,133 +236,115 @@ try: #only for develop #decoded = "ZVEI2: 25832" #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=III(mit NA,ohneSIGNAL)) CRC correct\n'" + #decoded = "POCSAG1200: Address: 1234567 Function: 1 Alpha: XXMSG MEfeweffsjh" #time.sleep(1) - - if True: #if input data avalable + timestamp = int(time.time())#Get Timestamp - timestamp = int(time.time())#Get Timestamp + #FMS Decoder Section + #check FMS: -> check CRC -> validate -> check double alarm -> log + if "FMS:" in decoded: + logging.debug("recieved FMS") - #FMS Decoder Section - #check FMS: -> check CRC -> validate -> check double alarm -> log - if "FMS:" in decoded: - logging.debug("recieved FMS") - - fms_service = decoded[19] #Organisation - fms_country = decoded[36] #Bundesland - fms_location = decoded[65:67] #Ort - fms_vehicle = decoded[72:76] #Fahrzeug - fms_status = decoded[84] #Status - fms_direction = decoded[101] #Richtung - fms_tsi = decoded[114:117] #Taktische Kruzinformation - - if "CRC correct" in decoded: #check CRC is correct - fms_id = fms_service+fms_country+fms_location+fms_vehicle+fms_status+fms_direction #build FMS id - if re.search("[0-9a-f]{8}[0-9a-f]{1}[01]{1}", fms_id): #if FMS is valid - if fms_id == fms_id_old and timestamp < fms_time_old + globals.config.getint("BOSWatch", "fms_double_ignore_time"): #check for double alarm - logging.warning("FMS double alarm: %s within %s second(s)", fms_id_old, timestamp-fms_time_old) - fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new - else: - logging.info("FMS:%s Status:%s Richtung:%s TKI:%s", fms_id[0:8], fms_status, fms_direction, fms_tsi) - data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "tsi":fms_tsi} - throwAlarm("FMS",data) - - fms_id_old = fms_id #save last id - fms_time_old = timestamp #save last time + fms_service = decoded[19] #Organisation + fms_country = decoded[36] #Bundesland + fms_location = decoded[65:67] #Ort + fms_vehicle = decoded[72:76] #Fahrzeug + fms_status = decoded[84] #Status + fms_direction = decoded[101] #Richtung + fms_tsi = decoded[114:117] #Taktische Kruzinformation + + if "CRC correct" in decoded: #check CRC is correct + fms_id = fms_service+fms_country+fms_location+fms_vehicle+fms_status+fms_direction #build FMS id + if re.search("[0-9a-f]{8}[0-9a-f]{1}[01]{1}", fms_id): #if FMS is valid + if fms_id == fms_id_old and timestamp < fms_time_old + globals.config.getint("BOSWatch", "fms_double_ignore_time"): #check for double alarm + logging.warning("FMS double alarm: %s within %s second(s)", fms_id_old, timestamp-fms_time_old) + fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new else: - logging.warning("No valid FMS: %s", fms_id) - else: - logging.warning("FMS CRC incorrect") + logging.info("FMS:%s Status:%s Richtung:%s TKI:%s", fms_id[0:8], fms_status, fms_direction, fms_tsi) + data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "tsi":fms_tsi} + throwAlarm("FMS",data) - - #ZVEI Decoder Section - #check ZVEI: -> validate -> check double alarm -> log - if "ZVEI2:" in decoded: - logging.debug("recieved ZVEI") - - zvei_id = decoded[7:12] #ZVEI Code - if re.search("[0-9F]{5}", zvei_id): #if ZVEI is valid - if zvei_id == zvei_id_old and timestamp < zvei_time_old + globals.config.getint("BOSWatch", "zvei_double_ignore_time"): #check for double alarm - logging.warning("ZVEI double alarm: %s within %s second(s)", zvei_id_old, timestamp-zvei_time_old) - zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new - else: - logging.info("5-Ton: %s", zvei_id) - data = {"zvei":zvei_id} - throwAlarm("ZVEI",data) - - zvei_id_old = zvei_id #save last id - zvei_time_old = timestamp #save last time + fms_id_old = fms_id #save last id + fms_time_old = timestamp #save last time else: - logging.warning("No valid ZVEI: %s", zvei_id) - - - #POCSAG512 Decoder Section - #check POCSAG512: -> validate -> check double alarm -> log - #POCSAG512: Address: 1234567 Function: 1 Alpha: XXMSG MEfeweffsjh + logging.warning("No valid FMS: %s", fms_id) + else: + logging.warning("FMS CRC incorrect") + + + #ZVEI Decoder Section + #check ZVEI: -> validate -> check double alarm -> log + if "ZVEI2:" in decoded: + logging.debug("recieved ZVEI") + + zvei_id = decoded[7:12] #ZVEI Code + if re.search("[0-9F]{5}", zvei_id): #if ZVEI is valid + if zvei_id == zvei_id_old and timestamp < zvei_time_old + globals.config.getint("BOSWatch", "zvei_double_ignore_time"): #check for double alarm + logging.warning("ZVEI double alarm: %s within %s second(s)", zvei_id_old, timestamp-zvei_time_old) + zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new + else: + logging.info("5-Ton: %s", zvei_id) + data = {"zvei":zvei_id} + throwAlarm("ZVEI",data) + + zvei_id_old = zvei_id #save last id + zvei_time_old = timestamp #save last time + else: + logging.warning("No valid ZVEI: %s", zvei_id) + + + #POCSAG Decoder Section + #check POCSAG -> validate -> check double alarm -> log + if "POCSAG" in decoded: + logging.debug("recieved POCSAG") + bitrate = 0 + if "POCSAG512:" in decoded: - logging.debug("recieved POCSAG512") - - poc_id = decoded[20:27] #POC Code + bitrate = 512 + poc_id = decoded[20:27] poc_sub = decoded[39].replace("3", "4").replace("2", "3").replace("1", "2").replace("0", "1") + + elif "POCSAG1200:" in decoded: + bitrate = 1200 + poc_id = decoded[21:28] + poc_sub = decoded[40].replace("3", "4").replace("2", "3").replace("1", "2").replace("0", "1") + + elif "POCSAG2400:" in decoded: + bitrate = 2400 + poc_id = decoded[21:28] + poc_sub = decoded[40].replace("3", "4").replace("2", "3").replace("1", "2").replace("0", "1") + + if bitrate is 0: + logging.warning("POCSAG Bitrate not found") + else: + logging.debug("POCSAG Bitrate: %s", bitrate) + if "Alpha:" in decoded: #check if there is a text message poc_text = decoded.split('Alpha: ')[1].strip().rstrip('').strip() else: poc_text = "" if re.search("[0-9]{7}", poc_id): #if POC is valid - if poc_id >= globals.config.getint("BOSWatch", "poc_filter_range_start"): - if poc_id <= globals.config.getint("BOSWatch", "poc_filter_range_end"): + if int(poc_id) >= globals.config.getint("BOSWatch", "poc_filter_range_start"): + if int(poc_id) <= globals.config.getint("BOSWatch", "poc_filter_range_end"): if poc_id == poc_id_old and timestamp < poc_time_old + globals.config.getint("BOSWatch", "poc_double_ignore_time"): #check for double alarm - logging.warning("POC512 double alarm: %s within %s second(s)", poc_id_old, timestamp-poc_time_old) + logging.warning("POCSAG%s double alarm: %s within %s second(s)", bitrate, poc_id_old, timestamp-poc_time_old) poc_time_old = timestamp #in case of double alarm, poc_double_ignore_time set new else: - logging.info("POCSAG512: %s %s %s ", poc_id, poc_sub, poc_text) - data = {"ric":poc_id, "function":poc_sub, "msg":poc_text} + logging.info("POCSAG%s: %s %s %s ", bitrate, poc_id, poc_sub, poc_text) + data = {"ric":poc_id, "function":poc_sub, "msg":poc_text, "bitrate":bitrate} throwAlarm("POC",data) poc_id_old = poc_id #save last id poc_time_old = timestamp #save last time else: - logging.warning("POCSAG512: %s out of filter range (high)", poc_id) + logging.warning("POCSAG%s: %s out of filter range (high)", bitrate, poc_id) else: - logging.warning("POCSAG512: %s out of filter range (low)", poc_id) + logging.warning("POCSAG%s: %s out of filter range (low)", bitrate, poc_id) else: - logging.warning("No valid POCSAG512: %s", poc_id) - + logging.warning("No valid POCSAG%s RIC: %s", bitrate, poc_id) - #POCSAG1200 Decoder Section - #check POCSAG1200: -> validate -> check double alarm -> log - #POCSAG1200: Address: 1234567 Function: 1 Alpha: XXMSG MEfeweffsjh - if "POCSAG1200:" in decoded: - logging.debug("recieved POCSAG1200") - - poc_id = decoded[21:28] #POC Code - poc_sub = decoded[40].replace("3", "4").replace("2", "3").replace("1", "2").replace("0", "1") - if "Alpha:" in decoded: #check if there is a text message - poc_text = decoded.split('Alpha: ')[1].strip().rstrip('').strip() - else: - poc_text = "" - - if re.search("[0-9]{7}", poc_id): #if POC is valid - if poc_id >= globals.config.getint("BOSWatch", "poc_filter_range_start"): - if poc_id <= globals.config.getint("BOSWatch", "poc_filter_range_end"): - if poc_id == poc_id_old and timestamp < poc_time_old + globals.config.getint("BOSWatch", "poc_double_ignore_time"): #check for double alarm - logging.warning("POC1200 double alarm: %s within %s second(s)", poc_id_old, timestamp-poc_time_old) - poc_time_old = timestamp #in case of double alarm, poc_double_ignore_time set new - else: - logging.info("POCSAG1200: %s %s %s", poc_id, poc_sub, poc_text) - data = {"ric":poc_id, "function":poc_sub, "msg":poc_text} - throwAlarm("POC",data) - - poc_id_old = poc_id #save last id - poc_time_old = timestamp #save last time - else: - logging.warning("POCSAG1200: %s out of filter range", poc_id) - else: - logging.warning("POCSAG1200: %s out of filter range", poc_id) - else: - logging.warning("No valid POCSAG1200: %s", poc_id) - except KeyboardInterrupt: logging.warning("Keyboard Interrupt") except: diff --git a/pluginloader.py b/pluginloader.py index 2d0e7db..d3efc6b 100644 --- a/pluginloader.py +++ b/pluginloader.py @@ -8,27 +8,34 @@ import os def getPlugins(): - PluginFolder = globals.script_path+"/plugins" - plugins = [] - for i in os.listdir(PluginFolder): - location = os.path.join(PluginFolder, i) - # plugins have to be a subdir with MainModule, if not skip - if not os.path.isdir(location) or not i + ".py" in os.listdir(location): - continue + try: + logging.debug("Search in Plugin Folder") + PluginFolder = globals.script_path+"/plugins" + plugins = [] + for i in os.listdir(PluginFolder): + location = os.path.join(PluginFolder, i) + # plugins have to be a subdir with MainModule, if not skip + if not os.path.isdir(location) or not i + ".py" in os.listdir(location): + continue + + # is the plugin enabled in the config-file? + try: + if globals.config.getint("Plugins", i): + info = imp.find_module(i, [location]) + plugins.append({"name": i, "info": info}) + logging.debug("Plugin [ENABLED ] %s", i) + else: + logging.debug("Plugin [DISABLED] %s ", i) + except: #no entry for plugin found in config-file, skip + logging.warning("Plugin [NO CONF ] %s", i) + except: + logging.exception("Error during Plugin search") - # is the plugin enabled in the config-file? - try: - if globals.config.getint("Plugins", i): - info = imp.find_module(i, [location]) - plugins.append({"name": i, "info": info}) - logging.debug("Plugin [ENABLED ] %s", i) - else: - logging.debug("Plugin [DISABLED] %s ", i) - except: #no entry for plugin found in config-file, skip - logging.warning("Plugin [NO CONF ] %s", i) - return plugins def loadPlugin(plugin): - logging.debug("load Plugin: %s", plugin["name"]) - return imp.load_module(plugin["name"], *plugin["info"]) \ No newline at end of file + try: + logging.debug("load Plugin: %s", plugin["name"]) + return imp.load_module(plugin["name"], *plugin["info"]) + except: + logging.exception("cannot load Plugin: %s", plugin["name"]) \ No newline at end of file diff --git a/plugins/BosMon/BosMon.py b/plugins/BosMon/BosMon.py index c4923bc..3c37435 100644 --- a/plugins/BosMon/BosMon.py +++ b/plugins/BosMon/BosMon.py @@ -18,13 +18,11 @@ def run(typ,freq,data): except: logging.exception("cannot read config file") - + ########## User Plugin CODE ########## if typ == "FMS": - logging.warning("FMS not implemented") - + logging.warning("%s not supported", typ) elif typ == "ZVEI": - logging.warning("ZVEI not implemented") - + logging.warning("%s not supported", typ) elif typ == "POC": logging.debug("Start POC to BosMon") try: @@ -39,7 +37,7 @@ def run(typ,freq,data): headers = {} headers['Content-type'] = "application/x-www-form-urlencoded" headers['Accept'] = "text/plain" - if bosmon_user: + if globals.config.get("BosMon", "bosmon_user"): headers['Authorization'] = "Basic {0}".format(base64.b64encode("{0}:{1}".format(globals.config.get("BosMon", "bosmon_user"), globals.config.get("BosMon", "bosmon_password")))) httprequest = httplib.HTTPConnection(globals.config.get("BosMon", "bosmon_server"), globals.config.get("BosMon", "bosmon_port")) httprequest.request("POST", "/telegramin/"+bosmon_channel+"/input.xml", params, headers) @@ -51,6 +49,8 @@ def run(typ,freq,data): except: logging.error("POC to BosMon failed") else: - logging.warning("undefined typ '%s'", typ) + logging.warning("Invalid Typ: %s", typ) + ########## User Plugin CODE ########## + except: logging.exception("") diff --git a/plugins/MySQL/MySQL.py b/plugins/MySQL/MySQL.py index b3eb8ff..621b401 100644 --- a/plugins/MySQL/MySQL.py +++ b/plugins/MySQL/MySQL.py @@ -40,7 +40,7 @@ def run(typ,freq,data): cursor.execute("INSERT INTO "+globals.config.get("MySQL","tablePOC")+" (time,ric,funktion,text) VALUES (NOW(),%s,%s,%s)",(data["ric"],data["function"],data["msg"])) else: - logging.warning(typ + " not supportet") + logging.warning("Invalid Typ: %s", typ) except: logging.exception("cannot Insert %s", typ) diff --git a/plugins/httpRequest/httpRequest.py b/plugins/httpRequest/httpRequest.py index 4d294d7..c0655ed 100644 --- a/plugins/httpRequest/httpRequest.py +++ b/plugins/httpRequest/httpRequest.py @@ -23,6 +23,8 @@ import globals # Global variables # usable Loglevels debug|info|warning|error|exception|critical # if you use .exception in Try:Exception: Construct, it logs the Python EX.message too +import httplib #for the HTTP request + def run(typ,freq,data): try: #ConfigParser @@ -32,15 +34,38 @@ def run(typ,freq,data): logging.debug(" - %s = %s", key, val) except: logging.exception("cannot read config file") - - if typ == "FMS": - logging.debug(typ + " not supported") - elif typ == "ZVEI": - logging.debug(typ + " not supported") - elif typ == "POC": - logging.debug(typ + " not supported") + +########## User Plugin CODE ########## + try: + logging.debug("send %s HTTP request", typ) + + if typ == "FMS": + httprequest = httplib.HTTPConnection(globals.config.get("httpRequest", "fms_url")) + httprequest.request("HEAD", "/") + elif typ == "ZVEI": + httprequest = httplib.HTTPConnection(globals.config.get("httpRequest", "zvei_url")) + httprequest.request("HEAD", "/") + elif typ == "POC": + httprequest = httplib.HTTPConnection(globals.config.get("httpRequest", "poc_url")) + httprequest.request("HEAD", "/") + else: + logging.warning("Invalid Typ: %s", typ) + + except: + loggin.exception("cannot send HTTP request") else: - logging.warning(typ + " not supported") + + try: + httpresponse = httprequest.getresponse() + if str(httpresponse.status) == "200": #Check HTTP Response an print a Log or Error + logging.debug("HTTP response: %s - %s" , str(httpresponse.status), str(httpresponse.reason)) + else: + logging.warning("HTTP response: %s - %s" , str(httpresponse.status), str(httpresponse.reason)) + except NameError: #if var httprequest does not exist + logging.exception("no HTTP request been sended") + except: #otherwise + logging.exception("cannot get HTTP response") +########## User Plugin CODE ########## except: logging.exception("unknown error") diff --git a/plugins/interface.txt b/plugins/interface.txt index 73d9c5a..016f079 100644 --- a/plugins/interface.txt +++ b/plugins/interface.txt @@ -5,7 +5,8 @@ freq = [FREQ(Hz)] data = {"KEY1":"VALUE1","KEY2":"VALUE2"} als Python Dict -Follgende Daten sind bei der jeweiligen Alarm-Art enthalten: +Follgende Daten sind bei der jeweiligen Alarm-Art enthalten +und per data["OPTION"] abrufbar: ZVEI: - zvei @@ -20,12 +21,16 @@ POCSAG: - ric - function - msg +- bitrate Es stehen folgende globale Objecte zur Verfügung: 1.) import logging # Global logger logging - Object +Nachricht ins Log per: logging.LOGLEVEL("MESSAGE") +Mögliche Loglevel: debug|info|warning|error|exception|critical 2.) import globals # Global variables -config - Object (typ: ConfigParser, stellt config.ini bereit) \ No newline at end of file +config - Object (typ: ConfigParser, stellt config.ini bereit) +VALUE = globals.config.get("SECTION", "OPTION") \ No newline at end of file diff --git a/plugins/template/template.py b/plugins/template/template.py index b7db7d8..5a899fe 100644 --- a/plugins/template/template.py +++ b/plugins/template/template.py @@ -32,15 +32,17 @@ def run(typ,freq,data): logging.debug(" - %s = %s", key, val) except: logging.exception("cannot read config file") - + +########## User Plugin CODE ########## if typ == "FMS": - logging.debug(typ + " not supported") + logging.warning("%s not supported", typ) elif typ == "ZVEI": - logging.debug(typ + " not supported") + logging.warning("%s not supported", typ) elif typ == "POC": - logging.debug(typ + " not supported") + logging.warning("%s not supported", typ) else: - logging.warning(typ + " not supported") - + logging.warning("Invalid Typ: %s", typ) +########## User Plugin CODE ########## + except: logging.exception("unknown error")