From 2938bd08ce06155d8e1e98c3dc50e58ab0bbee36 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Sat, 23 May 2015 11:12:45 +0200 Subject: [PATCH 01/50] edit readme.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 22b50d9..c53bc91 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,14 @@ For each Plugin that requires configurations a own Section with his Name is avai For the other Functions see "Usage" below. +##### Filtering Functions +For the Filter Functions see Section `[Filters]` +Syntax: INDIVIDUAL_NAME = TYP;PLUGIN;REGEX +TYP = FMS|ZVEI|POC +PLUGIN = Name of the Plugin +REGEX = Filtering with RegEx +my_own_Filter = ZVEI;template;25[0-9F]{3} #all ZVEI alarm to Plugin template where 25### + ##### Web Frontend Put the Files in Folder /wwww/ into your local Webserver Folder (/var/www/). Now you must edit the "config.php" with your Userdata to your local Database. From a628be6ff2dc49c8039b60a142502673ec8f5757 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Sun, 24 May 2015 12:21:06 +0200 Subject: [PATCH 02/50] edit config.ini --- config/config.template.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.template.ini b/config/config.template.ini index febde63..6f3f7ea 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -27,7 +27,7 @@ poc_filter_range_end = 9999999 [Filters] #No Filter for a Typ/Plugin Combination = all Data pass #INDIVIDUAL_NAME = TYP;PLUGIN;REGEX -zvei_local_filter = ZVEI;template;25[0-9F]{3} +#testfilter = ZVEI;template;25[0-9F]{3} #only ZVEI to template with 25### [Plugins] From 72ef930c7f55234aa20c0e39899cf96869fda0e5 Mon Sep 17 00:00:00 2001 From: JHCD Date: Sun, 24 May 2015 21:24:26 +0200 Subject: [PATCH 03/50] simple filter for pocsag - implement allow-/deny-list - move rangefilter restructure config.ini to follow code-structure implemented switch to turn on/off regEx-filter --- boswatch.py | 7 +++-- config/config.template.ini | 32 ++++++++++++++++------ includes/alarmHandler.py | 10 +++++-- includes/decoders/fms.py | 2 +- includes/decoders/poc.py | 56 ++++++++++++++++++++++++++++---------- includes/decoders/zvei.py | 2 +- 6 files changed, 79 insertions(+), 30 deletions(-) diff --git a/boswatch.py b/boswatch.py index 3cf9867..abd1b82 100755 --- a/boswatch.py +++ b/boswatch.py @@ -142,8 +142,9 @@ try: pluginLoader.loadPlugins() #load filters - from includes import filter - filter.getFilters() + if globals.config.getint("BOSWatch","useRegExFilter"): + from includes import filter + filter.getFilters() try: #start rtl_fm @@ -175,7 +176,7 @@ try: #RAW Data from Multimon-NG #ZVEI2: 25832 #FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n' - decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout + #decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout #only for develop #decoded = "ZVEI2: 25832" diff --git a/config/config.template.ini b/config/config.template.ini index 6f3f7ea..b171c13 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -11,23 +11,39 @@ #50 = critical loglevel = 10 -#time to ignore same alarm in a row (sek) -fms_double_ignore_time = 5 +#Using RegEx-Filter (0|1) +#Filter-configuration in section "Filters" +useRegExFilter = 1 +[FMS] #time to ignore same alarm in a row (sek) -zvei_double_ignore_time = 5 +double_ignore_time = 5 +[ZVEI] #time to ignore same alarm in a row (sek) -poc_double_ignore_time = 10 -#start and end of the filter range -poc_filter_range_start = 0000000 -poc_filter_range_end = 9999999 +double_ignore_time = 5 + +[POC] +#time to ignore same alarm in a row (sek) +double_ignore_time = 5 + +#some very simple filters: +#Allow only this RICs (empty: allow all, separator ",") +#f.e.: allow_ric = 1234566,1234567,1234568 +allow_ric = +#Deny this RICs (empty: allow all, separator ",") +#f.e.: deny_ric = 1234566,1234567,1234568 +deny_ric = + +#start and end of an allowed filter range +filter_range_start = 0000000 +filter_range_end = 9999999 [Filters] #No Filter for a Typ/Plugin Combination = all Data pass #INDIVIDUAL_NAME = TYP;PLUGIN;REGEX -#testfilter = ZVEI;template;25[0-9F]{3} #only ZVEI to template with 25### +zvei_local_filter = ZVEI;template;25[0-9F]{3} [Plugins] diff --git a/includes/alarmHandler.py b/includes/alarmHandler.py index 6c648cf..cf796d0 100644 --- a/includes/alarmHandler.py +++ b/includes/alarmHandler.py @@ -8,8 +8,14 @@ from includes import globals # Global variables def processAlarm(typ,freq,data): logging.debug("[ ALARM ]") for pluginName, plugin in globals.pluginList.items(): - from includes import filter - if filter.checkFilters(data,typ,pluginName): + #if enabled use RegEx-Filter + if globals.config.getint("BOSWatch","useRegExFilter"): + from includes import filter + if filter.checkFilters(data,typ,pluginName): + logging.debug("call Plugin: %s", pluginName) + plugin.run(typ,freq,data) + logging.debug("return from: %s", pluginName) + else: logging.debug("call Plugin: %s", pluginName) plugin.run(typ,freq,data) logging.debug("return from: %s", pluginName) diff --git a/includes/decoders/fms.py b/includes/decoders/fms.py index 2c983ef..9428f93 100644 --- a/includes/decoders/fms.py +++ b/includes/decoders/fms.py @@ -23,7 +23,7 @@ def decode(freq, decoded): 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 == globals.fms_id_old and timestamp < globals.fms_time_old + globals.config.getint("BOSWatch", "fms_double_ignore_time"): #check for double alarm + if fms_id == globals.fms_id_old and timestamp < globals.fms_time_old + globals.config.getint("FMS", "double_ignore_time"): #check for double alarm logging.info("FMS double alarm: %s within %s second(s)", globals.fms_id_old, timestamp-globals.fms_time_old) globals.fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new else: diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index 0aa9e76..593bd95 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -7,9 +7,36 @@ import re #Regex for validation from includes import globals # Global variables +# Simple Filter +def isAllowed(poc_id): + """Simple filter for POCSAG""" + # 1.) If allowed RICs is set, only they will path, + # If RIC is the right one return True, else False + if globals.config.get("POC", "allow_ric"): + if poc_id in globals.config.get("POC", "allow_ric"): + logging.debug("RIC %s is allowed", poc_id) + return True + else: + logging.debug("RIC %s is not in the allowed list", poc_id) + return False + # 2.) If denied RIC, return False + elif poc_id in globals.config.get("POC", "deny_ric"): + logging.debug("RIC %s is denied by config.ini", poc_id) + return False + # 3.) Check Range, return False if outside def. range + elif int(poc_id) < globals.config.getint("POC", "filter_range_start"): + logging.debug("RIC %s out of filter range (start)", poc_id) + return False + elif int(poc_id) > globals.config.getint("POC", "filter_range_end"): + logging.debug("RIC %s out of filter range (end)", poc_id) + return False + return True + + #POCSAG Decoder Function #validate -> check double alarm -> log def decode(freq, decoded): + """Decode for structure of typ POCSAG""" bitrate = 0 timestamp = int(time.time())#Get Timestamp @@ -39,22 +66,21 @@ def decode(freq, decoded): poc_text = "" if re.search("[0-9]{7}", poc_id): #if POC is valid - 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 == globals.poc_id_old and timestamp < globals.poc_time_old + globals.config.getint("BOSWatch", "poc_double_ignore_time"): #check for double alarm - logging.info("POCSAG%s double alarm: %s within %s second(s)", bitrate, globals.poc_id_old, timestamp-globals.poc_time_old) - globals.poc_time_old = timestamp #in case of double alarm, poc_double_ignore_time set new - else: - 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} - from includes import alarmHandler - alarmHandler.processAlarm("POC",freq,data) - - globals.poc_id_old = poc_id #save last id - globals.poc_time_old = timestamp #save last time + if isAllowed(poc_id): + #check for double alarm + if poc_id == globals.poc_id_old and timestamp < globals.poc_time_old + globals.config.getint("POC", "double_ignore_time"): + logging.info("POCSAG%s double alarm: %s within %s second(s)", bitrate, globals.poc_id_old, timestamp-globals.poc_time_old) + #in case of double alarm, poc_double_ignore_time set new + globals.poc_time_old = timestamp else: - logging.info("POCSAG%s: %s out of filter range (high)", bitrate, poc_id) + 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} + from includes import alarmHandler + alarmHandler.processAlarm("POC",freq,data) + + globals.poc_id_old = poc_id #save last id + globals.poc_time_old = timestamp #save last time else: - logging.info("POCSAG%s: %s out of filter range (low)", bitrate, poc_id) + logging.info("POCSAG%s: %s is not allowed", bitrate, poc_id) else: logging.warning("No valid POCSAG%s RIC: %s", bitrate, poc_id) \ No newline at end of file diff --git a/includes/decoders/zvei.py b/includes/decoders/zvei.py index 81320dc..94d5912 100644 --- a/includes/decoders/zvei.py +++ b/includes/decoders/zvei.py @@ -14,7 +14,7 @@ def decode(freq, decoded): zvei_id = decoded[7:12] #ZVEI Code if re.search("[0-9F]{5}", zvei_id): #if ZVEI is valid - if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("BOSWatch", "zvei_double_ignore_time"): #check for double alarm + if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("ZVEI", "double_ignore_time"): #check for double alarm logging.info("ZVEI double alarm: %s within %s second(s)", globals.zvei_id_old, timestamp-globals.zvei_time_old) globals.zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new else: From 1ccc5bb4023de5fef2c12329644d788f0e382166 Mon Sep 17 00:00:00 2001 From: JHCD Date: Sun, 24 May 2015 21:27:50 +0200 Subject: [PATCH 04/50] enable pro-working --- boswatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boswatch.py b/boswatch.py index abd1b82..a703845 100755 --- a/boswatch.py +++ b/boswatch.py @@ -176,7 +176,7 @@ try: #RAW Data from Multimon-NG #ZVEI2: 25832 #FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n' - #decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout + decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout #only for develop #decoded = "ZVEI2: 25832" From 9c3c1f5df41b747633be6b570d142f6d1e03aea3 Mon Sep 17 00:00:00 2001 From: JHCD Date: Sun, 24 May 2015 23:09:38 +0200 Subject: [PATCH 05/50] implement FMS in BosMon-plugin --- boswatch.py | 10 +++++++--- includes/decoders/fms.py | 2 +- plugins/BosMon/BosMon.py | 36 ++++++++++++++++++++++++------------ 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/boswatch.py b/boswatch.py index a703845..f6cc9db 100755 --- a/boswatch.py +++ b/boswatch.py @@ -180,9 +180,13 @@ 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: Hello World" - #time.sleep(1) + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'" + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'" + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=II (ohneNA,mit SIGNAL)) CRC correct\n'" + decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct\n'" + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=IV (mit NA,mit SIGNAL)) CRC correct\n'" + #decoded = "POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World" + time.sleep(1) from includes import decoder decoder.decode(args.freq, decoded) diff --git a/includes/decoders/fms.py b/includes/decoders/fms.py index 9428f93..934c7da 100644 --- a/includes/decoders/fms.py +++ b/includes/decoders/fms.py @@ -27,7 +27,7 @@ def decode(freq, decoded): logging.info("FMS double alarm: %s within %s second(s)", globals.fms_id_old, timestamp-globals.fms_time_old) globals.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) + logging.info("FMS:%s Status:%s Richtung:%s TSI:%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} from includes import alarmHandler alarmHandler.processAlarm("FMS",freq,data) diff --git a/plugins/BosMon/BosMon.py b/plugins/BosMon/BosMon.py index d22e5a0..8793ec0 100644 --- a/plugins/BosMon/BosMon.py +++ b/plugins/BosMon/BosMon.py @@ -43,22 +43,34 @@ def run(typ,freq,data): logging.debug("connect to BosMon") httprequest = httplib.HTTPConnection(globals.config.get("BosMon", "bosmon_server"), globals.config.get("BosMon", "bosmon_port")) #debug-level to shell (0=no debug|1) - httprequest.set_debuglevel(0) + httprequest.set_debuglevel(1) except: logging.exception("cannot connect to BosMon") else: if typ == "FMS": - logging.warning("%s not supported", typ) - #logging.debug("Start FMS to BosMon") - #try: + logging.debug("Start FMS to BosMon") + try: #BosMon-Telegramin expected assembly group, direction and tsi in one field - #structure: - #params = urllib.urlencode({'type':'fms', 'address':data["fms"], 'status':data["status"], 'info':'0', 'flags':'0'}) - #logging.debug(" - Params: %s", params) - #bosMonRequest(httprequest, params, headers) - #except: - #logging.error("FMS to BosMon failed") + #structure: Byte 1: assembly group; Byte 2: Direction; Byte 3+4: tactic short info + info = 0 + #assembly group: + info = info + 1 # + b0001 (Assumption: is in every time 1 (no output from multimon-ng)) + #direction: + if data["direction"] == "1": + info = info + 2 # + b0010 + #tsi: + if "IV" in data["tsi"]: + info = info + 12 # + b1100 + elif "III" in data["tsi"]: + info = info + 8 # + b1000 + elif "II" in data["tsi"]: + info = info + 4 # + b0100 + params = urllib.urlencode({'type':'fms', 'address':data["fms"], 'status':data["status"], 'info':info, 'flags':'0'}) + logging.debug(" - Params: %s", params) + bosMonRequest(httprequest, params, headers) + except: + logging.exception("FMS to BosMon failed") elif typ == "ZVEI": logging.debug("Start ZVEI to BosMon") @@ -67,7 +79,7 @@ def run(typ,freq,data): logging.debug(" - Params: %s", params) bosMonRequest(httprequest, params, headers) except: - logging.error("ZVEI to BosMon failed") + logging.exception("ZVEI to BosMon failed") elif typ == "POC": logging.debug("Start POC to BosMon") @@ -78,7 +90,7 @@ def run(typ,freq,data): logging.debug(" - Params: %s", params) bosMonRequest(httprequest, params, headers) except: - logging.error("POC to BosMon failed") + logging.exception("POC to BosMon failed") else: logging.warning("Invalid Typ: %s", typ) From 76752b0085f32cb83ad9d626170c0f1ad8de76c6 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Sun, 24 May 2015 23:22:32 +0200 Subject: [PATCH 06/50] edit config.ini --- config/config.template.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.template.ini b/config/config.template.ini index b171c13..5ea121a 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -43,7 +43,7 @@ filter_range_end = 9999999 [Filters] #No Filter for a Typ/Plugin Combination = all Data pass #INDIVIDUAL_NAME = TYP;PLUGIN;REGEX -zvei_local_filter = ZVEI;template;25[0-9F]{3} +##testfilter = ZVEI;template;25[0-9F]{3} #only ZVEI to template with 25### [Plugins] From 623d8967aaba2996c569857750262d1c5f611304 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Sun, 24 May 2015 23:34:09 +0200 Subject: [PATCH 07/50] edit readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c53bc91..bad33ae 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ unless you are developer you can use the develop-Branch - may be unstable! - FMS, ZVEI and POCSAG512/1200/2400 decoding and Displaying - Plugin support for easy Functions extension - Filtering double alarms with adjustable time -- Filtering Range of POCSAG RIC´s -- Filtering Data for each Typ/Plugin combination +- Filtering Allowed, Denied and Range of POCSAG RIC´s +- Filtering Data for each Typ/Plugin combination with RegEX - All configurations in seperate config File - Data validation (plausibility test) - Logfiles for better Troubleshooting @@ -30,7 +30,7 @@ unless you are developer you can use the develop-Branch - may be unstable! ###Plugins ##### Implemented Plugins: - MySQL (insert Data into MySQL Database [FMS|ZVEI|POC]) -- BosMon (send Data to BosMon Server [ZVEI|POC]) +- BosMon (send Data to BosMon Server [FMS|ZVEI|POC]) - httpRequest (send a request to an URL [FMS|ZVEI|POC]) ##### Plugins for the Future: From d4609476853cbb94105f5f875a22054e37328ab2 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Sun, 24 May 2015 23:52:10 +0200 Subject: [PATCH 08/50] little change filter.py --- config/config.template.ini | 6 +++++- includes/filter.py | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/config/config.template.ini b/config/config.template.ini index 5ea121a..41667f9 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -41,9 +41,12 @@ filter_range_end = 9999999 [Filters] +#RegEX Filter Configuration #No Filter for a Typ/Plugin Combination = all Data pass #INDIVIDUAL_NAME = TYP;PLUGIN;REGEX -##testfilter = ZVEI;template;25[0-9F]{3} #only ZVEI to template with 25### + +#only ZVEI to template with 25### +#testfilter = ZVEI;template;25[0-9F]{3} [Plugins] @@ -51,6 +54,7 @@ filter_range_end = 9999999 MySQL = 0 BosMon = 0 httpRequest = 0 + # for developing template-module template = 0 diff --git a/includes/filter.py b/includes/filter.py index 6de5d44..670e668 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -24,16 +24,16 @@ def checkFilters(data,typ,plugin): logging.debug("search Filter for %s to %s", typ, plugin) #extract the correct data for filtering - if typ == "FMS": data = data["fms"] - if typ == "ZVEI": data = data["zvei"] - if typ == "POC": data = data["poc"] + #if typ == "FMS": data = data["fms"] + #if typ == "ZVEI": data = data["zvei"] + #if typ == "POC": data = data["poc"] foundFilter = False for i in globals.filterList: if i["typ"] == typ and i["plugin"] == plugin: foundFilter = True logging.debug("found Filter: %s = %s", i["name"], i["regex"]) - if re.search(i["regex"], data): + if re.search(i["regex"], data[typ.lower()]): logging.debug("Filter passed: %s", i["name"]) return True else: From 3e59ca2127edd557884c2c61ba509ecd4e0d1191 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Sun, 24 May 2015 23:59:00 +0200 Subject: [PATCH 09/50] revert edit in filter.py --- includes/filter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/filter.py b/includes/filter.py index 670e668..6de5d44 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -24,16 +24,16 @@ def checkFilters(data,typ,plugin): logging.debug("search Filter for %s to %s", typ, plugin) #extract the correct data for filtering - #if typ == "FMS": data = data["fms"] - #if typ == "ZVEI": data = data["zvei"] - #if typ == "POC": data = data["poc"] + if typ == "FMS": data = data["fms"] + if typ == "ZVEI": data = data["zvei"] + if typ == "POC": data = data["poc"] foundFilter = False for i in globals.filterList: if i["typ"] == typ and i["plugin"] == plugin: foundFilter = True logging.debug("found Filter: %s = %s", i["name"], i["regex"]) - if re.search(i["regex"], data[typ.lower()]): + if re.search(i["regex"], data): logging.debug("Filter passed: %s", i["name"]) return True else: From 21ad7e1a4a5d5cbd21e9c2f4309af4d940d13ab0 Mon Sep 17 00:00:00 2001 From: JHCD Date: Mon, 25 May 2015 00:11:35 +0200 Subject: [PATCH 10/50] add some documentation --- plugins/BosMon/BosMon.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/BosMon/BosMon.py b/plugins/BosMon/BosMon.py index 8793ec0..d9cc727 100644 --- a/plugins/BosMon/BosMon.py +++ b/plugins/BosMon/BosMon.py @@ -43,7 +43,7 @@ def run(typ,freq,data): logging.debug("connect to BosMon") httprequest = httplib.HTTPConnection(globals.config.get("BosMon", "bosmon_server"), globals.config.get("BosMon", "bosmon_port")) #debug-level to shell (0=no debug|1) - httprequest.set_debuglevel(1) + httprequest.set_debuglevel(0) except: logging.exception("cannot connect to BosMon") @@ -52,7 +52,8 @@ def run(typ,freq,data): logging.debug("Start FMS to BosMon") try: #BosMon-Telegramin expected assembly group, direction and tsi in one field - #structure: Byte 1: assembly group; Byte 2: Direction; Byte 3+4: tactic short info + #structure (binary as hex in base10): + # Byte 1: assembly group; Byte 2: Direction; Byte 3+4: tactic short info info = 0 #assembly group: info = info + 1 # + b0001 (Assumption: is in every time 1 (no output from multimon-ng)) @@ -66,6 +67,8 @@ def run(typ,freq,data): info = info + 8 # + b1000 elif "II" in data["tsi"]: info = info + 4 # + b0100 + # "I" is + b0000 => nothing to do... + params = urllib.urlencode({'type':'fms', 'address':data["fms"], 'status':data["status"], 'info':info, 'flags':'0'}) logging.debug(" - Params: %s", params) bosMonRequest(httprequest, params, headers) From 2b333975c51645f63354b1a141c81a662956d935 Mon Sep 17 00:00:00 2001 From: JHCD Date: Mon, 25 May 2015 00:13:34 +0200 Subject: [PATCH 11/50] add some documentation --- plugins/BosMon/BosMon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/BosMon/BosMon.py b/plugins/BosMon/BosMon.py index d9cc727..889dcb0 100644 --- a/plugins/BosMon/BosMon.py +++ b/plugins/BosMon/BosMon.py @@ -67,7 +67,7 @@ def run(typ,freq,data): info = info + 8 # + b1000 elif "II" in data["tsi"]: info = info + 4 # + b0100 - # "I" is + b0000 => nothing to do... + # "I" is nothing to do + b0000 params = urllib.urlencode({'type':'fms', 'address':data["fms"], 'status':data["status"], 'info':info, 'flags':'0'}) logging.debug(" - Params: %s", params) From 0d55903dec6bf09585cfd35213faed133f60bed1 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Mon, 25 May 2015 00:40:41 +0200 Subject: [PATCH 12/50] resolve F in zvei --- includes/decoders/zvei.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/includes/decoders/zvei.py b/includes/decoders/zvei.py index 94d5912..efbf661 100644 --- a/includes/decoders/zvei.py +++ b/includes/decoders/zvei.py @@ -13,7 +13,8 @@ def decode(freq, decoded): timestamp = int(time.time())#Get Timestamp zvei_id = decoded[7:12] #ZVEI Code - if re.search("[0-9F]{5}", zvei_id): #if ZVEI is valid + zvei_id = resolveWDHtone(zvei_id) #resolve F + if re.search("[0-9]{5}", zvei_id): #if ZVEI is valid if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("ZVEI", "double_ignore_time"): #check for double alarm logging.info("ZVEI double alarm: %s within %s second(s)", globals.zvei_id_old, timestamp-globals.zvei_time_old) globals.zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new @@ -26,4 +27,13 @@ def decode(freq, decoded): globals.zvei_id_old = zvei_id #save last id globals.zvei_time_old = timestamp #save last time else: - logging.warning("No valid ZVEI: %s", zvei_id) \ No newline at end of file + logging.warning("No valid ZVEI: %s", zvei_id) + + +def resolveWDHtone(zvei): + zvei_old = zvei + for i in range(1, 5): + if zvei[i] == "F": + zvei = zvei.replace("F",zvei[i-1],1) + logging.debug("resolve F: %s -> %s", zvei_old, zvei) + return zvei \ No newline at end of file From 42384407cecc59452f0d7de16f566acabaa91629 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Mon, 25 May 2015 10:25:31 +0200 Subject: [PATCH 13/50] edit resolveF() --- includes/decoders/zvei.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/includes/decoders/zvei.py b/includes/decoders/zvei.py index efbf661..0c5688e 100644 --- a/includes/decoders/zvei.py +++ b/includes/decoders/zvei.py @@ -13,7 +13,7 @@ def decode(freq, decoded): timestamp = int(time.time())#Get Timestamp zvei_id = decoded[7:12] #ZVEI Code - zvei_id = resolveWDHtone(zvei_id) #resolve F + zvei_id = removeF(zvei_id) #resolve F if re.search("[0-9]{5}", zvei_id): #if ZVEI is valid if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("ZVEI", "double_ignore_time"): #check for double alarm logging.info("ZVEI double alarm: %s within %s second(s)", globals.zvei_id_old, timestamp-globals.zvei_time_old) @@ -30,10 +30,11 @@ def decode(freq, decoded): logging.warning("No valid ZVEI: %s", zvei_id) -def resolveWDHtone(zvei): - zvei_old = zvei - for i in range(1, 5): - if zvei[i] == "F": - zvei = zvei.replace("F",zvei[i-1],1) - logging.debug("resolve F: %s -> %s", zvei_old, zvei) +def removeF(zvei): + if "F" in zvei: + zvei_old = zvei + for i in range(1, 5): + if zvei[i] == "F": + zvei = zvei.replace("F",zvei[i-1],1) + logging.debug("resolve F: %s -> %s", zvei_old, zvei) return zvei \ No newline at end of file From fa328676e894aab9e2f16096674aab73a230ea84 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Mon, 25 May 2015 10:28:35 +0200 Subject: [PATCH 14/50] bug in filter.py --- includes/alarmHandler.py | 3 +++ includes/filter.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/includes/alarmHandler.py b/includes/alarmHandler.py index cf796d0..870133a 100644 --- a/includes/alarmHandler.py +++ b/includes/alarmHandler.py @@ -8,6 +8,7 @@ from includes import globals # Global variables def processAlarm(typ,freq,data): logging.debug("[ ALARM ]") for pluginName, plugin in globals.pluginList.items(): + #if enabled use RegEx-Filter if globals.config.getint("BOSWatch","useRegExFilter"): from includes import filter @@ -15,8 +16,10 @@ def processAlarm(typ,freq,data): logging.debug("call Plugin: %s", pluginName) plugin.run(typ,freq,data) logging.debug("return from: %s", pluginName) + else: logging.debug("call Plugin: %s", pluginName) plugin.run(typ,freq,data) logging.debug("return from: %s", pluginName) + logging.debug("[END ALARM]") \ No newline at end of file diff --git a/includes/filter.py b/includes/filter.py index 6de5d44..6643d54 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -26,7 +26,7 @@ def checkFilters(data,typ,plugin): #extract the correct data for filtering if typ == "FMS": data = data["fms"] if typ == "ZVEI": data = data["zvei"] - if typ == "POC": data = data["poc"] + if typ == "POC": data = data["ric"] foundFilter = False for i in globals.filterList: From 3370b4387dfb6fda24378207155e8c158d900b37 Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Mon, 25 May 2015 10:37:20 +0200 Subject: [PATCH 15/50] regex filter - set dataField to check --- config/config.template.ini | 8 ++++++-- includes/filter.py | 9 ++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/config/config.template.ini b/config/config.template.ini index 41667f9..56cdd0f 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -43,10 +43,14 @@ filter_range_end = 9999999 [Filters] #RegEX Filter Configuration #No Filter for a Typ/Plugin Combination = all Data pass -#INDIVIDUAL_NAME = TYP;PLUGIN;REGEX +#INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;REGEX +TYP = the Data Typ (FMS|ZVEI|POC) +DATAFIELD = the field of the Data Array. See interface.txt for names. +PLUGIN = the name of the Plugin to call with this Filter. +REGEX = the RegEX #only ZVEI to template with 25### -#testfilter = ZVEI;template;25[0-9F]{3} +#testfilter = ZVEI;zvei;template;25[0-9F]{3} [Plugins] diff --git a/includes/filter.py b/includes/filter.py index 6643d54..24b1af7 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -14,7 +14,7 @@ def getFilters(): for key,val in globals.config.items("Filters"): logging.debug(" - %s = %s", key, val) filter = val.split(";") - globals.filterList.append({"name": key, "typ": filter[0], "plugin": filter[1], "regex": filter[2]}) + globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "regex": filter[3]}) except: logging.exception("cannot read config file") @@ -23,17 +23,12 @@ def checkFilters(data,typ,plugin): try: logging.debug("search Filter for %s to %s", typ, plugin) - #extract the correct data for filtering - if typ == "FMS": data = data["fms"] - if typ == "ZVEI": data = data["zvei"] - if typ == "POC": data = data["ric"] - foundFilter = False for i in globals.filterList: if i["typ"] == typ and i["plugin"] == plugin: foundFilter = True logging.debug("found Filter: %s = %s", i["name"], i["regex"]) - if re.search(i["regex"], data): + if re.search(i["regex"], data[i["dataField"]]): logging.debug("Filter passed: %s", i["name"]) return True else: From 4f79af944d22695300cdd2f01dd8c1ae288182e8 Mon Sep 17 00:00:00 2001 From: JHCD Date: Mon, 25 May 2015 18:07:44 +0200 Subject: [PATCH 16/50] Change logging to TimedRotatingFileHandler - rotate logfile at midnight - shutdown/close logfiles at script-end - small enhancement of the code-documentation --- boswatch.py | 129 ++++++++++++++++++++++++++----------- config/config.template.ini | 6 ++ 2 files changed, 97 insertions(+), 38 deletions(-) diff --git a/boswatch.py b/boswatch.py index f6cc9db..1ce3b91 100755 --- a/boswatch.py +++ b/boswatch.py @@ -1,14 +1,18 @@ #!/usr/bin/python # -*- coding: cp1252 -*- - -##### Info ##### +# # BOSWatch # Autor: Bastian Schroll -# Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG -# For more Information see the README.md -##### Info ##### +# +# + +""" +Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG +For more Information see the README.md +""" import logging +import logging.handlers import argparse #for parse the args import ConfigParser #for parse the config file @@ -18,43 +22,65 @@ import subprocess from includes import globals # Global variables +## +# This Class extended the TimedRotatingFileHandler with the possibility to change the backupCount after initialization. +## +class MyTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): + """Extended Version of TimedRotatingFileHandler""" + def setBackupCount(self, backupCount): + """Set/Change backupCount""" + self.backupCount = backupCount + +# # Programm +# try: try: - #create logger + # + # Script-pathes + # globals.script_path = os.path.dirname(os.path.abspath(__file__)) + # + # If necessary create Log-Path + # if not os.path.exists(globals.script_path+"/log/"): os.mkdir(globals.script_path+"/log/") - - #create new logger - logger = logging.getLogger() - logger.setLevel(logging.DEBUG) + + # + # Create new myLogger... + # + myLogger = logging.getLogger() + myLogger.setLevel(logging.DEBUG) #set log string format formatter = logging.Formatter('%(asctime)s - %(module)-12s [%(levelname)-8s] %(message)s', '%d.%m.%Y %H:%M:%S') #create a file logger - fh = logging.FileHandler(globals.script_path+"/log/boswatch.log", "w") - fh.setLevel(logging.DEBUG) #log level >= Debug + fh = MyTimedRotatingFileHandler(globals.script_path+"/log/boswatch.log", "midnight", interval=1, backupCount=999) + #Starts with log level >= Debug + #will be changed with config.ini-param later + fh.setLevel(logging.DEBUG) fh.setFormatter(formatter) - logger.addHandler(fh) + myLogger.addHandler(fh) #create a display logger ch = logging.StreamHandler() - ch.setLevel(logging.INFO) #log level >= info + #log level for display >= info + #will be changed later after parsing args + ch.setLevel(logging.INFO) ch.setFormatter(formatter) - logger.addHandler(ch) + myLogger.addHandler(ch) except: logging.exception("cannot create logger") else: - try: - #clear log - bos_log = open(globals.script_path+"/log/boswatch.log", "w") + try: + # + # Clear the logfiles + # + fh.doRollover() rtl_log = open(globals.script_path+"/log/rtl_fm.log", "w") mon_log = open(globals.script_path+"/log/multimon.log", "w") - bos_log.write("") rtl_log.write("") mon_log.write("") - bos_log.close() rtl_log.close() mon_log.close() logging.debug("BOSWatch has started") @@ -62,8 +88,10 @@ try: except: logging.exception("cannot clear Logfiles") - try: - #parse args + try: + # + # Parse args + # logging.debug("parse args") #With -h or --help you get the Args help #ArgsParser @@ -81,8 +109,10 @@ try: logging.error("cannot parse args") else: - try: - #display/log args + try: + # + # For debug display/log args + # logging.debug(" - Frequency: %s", args.freq) logging.debug(" - Device: %s", args.device) logging.debug(" - PPM Error: %s", args.error) @@ -119,8 +149,10 @@ try: except: logging.exception("cannot display/log args") - try: - #read config + try: + # + # Read config.ini + # logging.debug("reading config file") globals.config = ConfigParser.ConfigParser() globals.config.read(globals.script_path+"/config/config.ini") @@ -131,23 +163,33 @@ try: else: try: - #set the loglevel of the file handler - logging.debug("set loglevel of fileHandler") + # + # Set the loglevel and backupCount of the file handler + # + logging.debug("set loglevel of fileHandler to: %s",globals.config.getint("BOSWatch","loglevel") ) fh.setLevel(globals.config.getint("BOSWatch","loglevel")) + logging.debug("set backupCount of fileHandler to: %s", globals.config.getint("BOSWatch","backupCount")) + fh.setBackupCount(globals.config.getint("BOSWatch","backupCount")) except: logging.exception("cannot set loglevel of fileHandler") - #load plugins + # + # Load plugins + # from includes import pluginLoader pluginLoader.loadPlugins() - #load filters + # + # Load filters + # if globals.config.getint("BOSWatch","useRegExFilter"): from includes import filter filter.getFilters() try: - #start rtl_fm + # + # Start rtl_fm + # logging.debug("starting rtl_fm") rtl_fm = subprocess.Popen("rtl_fm -d "+str(args.device)+" -f "+str(args.freq)+" -M fm -s 22050 -p "+str(args.error)+" -E DC -F 0 -l "+str(args.squelch)+" -g 100", #stdin=rtl_fm.stdout, @@ -159,7 +201,9 @@ try: else: try: - #start multimon + # + # Start multimon + # logging.debug("starting multimon-ng") multimon_ng = subprocess.Popen("multimon-ng "+str(demodulation)+" -f alpha -t raw /dev/stdin - ", stdin=rtl_fm.stdout, @@ -173,20 +217,25 @@ try: logging.debug("start decoding") while True: - #RAW Data from Multimon-NG - #ZVEI2: 25832 - #FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n' + # + # Get decoded data from multimon-ng and call BOSWatch-decoder + # + + # RAW Data from Multimon-NG + # ZVEI2: 25832 + # FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n' + # POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout - #only for develop + # Test-strings 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=I (ohneNA,ohneSIGNAL)) CRC correct\n'" #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'" #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=II (ohneNA,mit SIGNAL)) CRC correct\n'" - decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct\n'" + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct\n'" #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=IV (mit NA,mit SIGNAL)) CRC correct\n'" #decoded = "POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World" - time.sleep(1) + #time.sleep(1) from includes import decoder decoder.decode(args.freq, decoded) @@ -207,4 +256,8 @@ finally: logging.warning("failed in clean-up routine") finally: logging.info("BOSWatch exit()") + # Close Logging + logging.shutdown() + fh.close() + ch.close() exit(0) diff --git a/config/config.template.ini b/config/config.template.ini index 56cdd0f..ba124d6 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -11,6 +11,12 @@ #50 = critical loglevel = 10 +#BOSWatch use a rotating logfile +#Rotating is at midnight +#You can set the backupCount here +#backupCount = 7 (keeps logfiles for a week) +backupCount = 7 + #Using RegEx-Filter (0|1) #Filter-configuration in section "Filters" useRegExFilter = 1 From d9b8ae303572730713439fa02bace157420e6bc6 Mon Sep 17 00:00:00 2001 From: Schrolli Date: Tue, 26 May 2015 07:19:24 +0200 Subject: [PATCH 17/50] little changes --- boswatch.py | 7 ++++--- config/config.template.ini | 8 ++++---- includes/filter.py | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/boswatch.py b/boswatch.py index 1ce3b91..cd52840 100755 --- a/boswatch.py +++ b/boswatch.py @@ -184,7 +184,7 @@ try: # if globals.config.getint("BOSWatch","useRegExFilter"): from includes import filter - filter.getFilters() + filter.loadFilters() try: # @@ -254,10 +254,11 @@ finally: logging.debug("exiting BOSWatch") except: logging.warning("failed in clean-up routine") - finally: - logging.info("BOSWatch exit()") + finally: # Close Logging + logging.debug("close Logging") logging.shutdown() fh.close() ch.close() + logging.info("BOSWatch exit()") exit(0) diff --git a/config/config.template.ini b/config/config.template.ini index ba124d6..8e738e2 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -50,10 +50,10 @@ filter_range_end = 9999999 #RegEX Filter Configuration #No Filter for a Typ/Plugin Combination = all Data pass #INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;REGEX -TYP = the Data Typ (FMS|ZVEI|POC) -DATAFIELD = the field of the Data Array. See interface.txt for names. -PLUGIN = the name of the Plugin to call with this Filter. -REGEX = the RegEX +#TYP = the Data Typ (FMS|ZVEI|POC) +#DATAFIELD = the field of the Data Array. See interface.txt for names. +#PLUGIN = the name of the Plugin to call with this Filter. +#REGEX = the RegEX #only ZVEI to template with 25### #testfilter = ZVEI;zvei;template;25[0-9F]{3} diff --git a/includes/filter.py b/includes/filter.py index 24b1af7..72a2aac 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -8,8 +8,8 @@ import re #Regex for Filter Check from includes import globals # Global variables -def getFilters(): - logging.debug("reading config file") +def loadFilters(): + logging.debug("loading filters") try: for key,val in globals.config.items("Filters"): logging.debug(" - %s = %s", key, val) From 297f27c81c330873809174446c220bf5072e570b Mon Sep 17 00:00:00 2001 From: Schrolli Date: Tue, 26 May 2015 07:57:34 +0200 Subject: [PATCH 18/50] little edit in logging --- boswatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boswatch.py b/boswatch.py index cd52840..5eb60f6 100755 --- a/boswatch.py +++ b/boswatch.py @@ -257,8 +257,8 @@ finally: finally: # Close Logging logging.debug("close Logging") + logging.info("BOSWatch exit()") logging.shutdown() fh.close() ch.close() - logging.info("BOSWatch exit()") exit(0) From be9aa3b7226e902224f3aabbcb697d4a39c99cd1 Mon Sep 17 00:00:00 2001 From: Schrolli Date: Tue, 26 May 2015 09:14:04 +0200 Subject: [PATCH 19/50] error handling in decoder.py --- includes/decoder.py | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/includes/decoder.py b/includes/decoder.py index b693120..f05fd0b 100644 --- a/includes/decoder.py +++ b/includes/decoder.py @@ -4,24 +4,27 @@ import logging def decode(freq, decoded): - - #FMS Decoder Section - #check FMS: -> check CRC -> validate -> check double alarm -> log - if "FMS:" in decoded: - logging.debug("recieved FMS") - from includes.decoders import fms - fms.decode(freq, decoded) + try: + #FMS Decoder Section + #check FMS: -> check CRC -> validate -> check double alarm -> log + if "FMS:" in decoded: + logging.debug("recieved FMS") + from includes.decoders import fms + fms.decode(freq, decoded) - #ZVEI Decoder Section - #check ZVEI: -> validate -> check double alarm -> log - if "ZVEI2:" in decoded: - logging.debug("recieved ZVEI") - from includes.decoders import zvei - zvei.decode(freq, decoded) - - #POCSAG Decoder Section - #check POCSAG -> validate -> check double alarm -> log - if "POCSAG" in decoded: - logging.debug("recieved POCSAG") - from includes.decoders import poc - poc.decode(freq, decoded) + #ZVEI Decoder Section + #check ZVEI: -> validate -> check double alarm -> log + if "ZVEI2:" in decoded: + logging.debug("recieved ZVEI") + from includes.decoders import zvei + zvei.decode(freq, decoded) + + #POCSAG Decoder Section + #check POCSAG -> validate -> check double alarm -> log + if "POCSAG" in decoded: + logging.debug("recieved POCSAG") + from includes.decoders import poc + poc.decode(freq, decoded) + + except: + logging.exception("cannot start decoder") From 0e952d6057b7c146ccfbb14863c6113bcbf581ae Mon Sep 17 00:00:00 2001 From: Schrolli Date: Tue, 26 May 2015 11:41:05 +0200 Subject: [PATCH 20/50] little debug and error edits --- includes/alarmHandler.py | 29 ++++++++++++++++------------- includes/decoder.py | 2 +- includes/filter.py | 2 +- includes/pluginLoader.py | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/includes/alarmHandler.py b/includes/alarmHandler.py index 870133a..51536a7 100644 --- a/includes/alarmHandler.py +++ b/includes/alarmHandler.py @@ -6,20 +6,23 @@ import logging from includes import globals # Global variables def processAlarm(typ,freq,data): - logging.debug("[ ALARM ]") - for pluginName, plugin in globals.pluginList.items(): - - #if enabled use RegEx-Filter - if globals.config.getint("BOSWatch","useRegExFilter"): - from includes import filter - if filter.checkFilters(data,typ,pluginName): + try: + logging.debug("[ ALARM ]") + for pluginName, plugin in globals.pluginList.items(): + + #if enabled use RegEx-Filter + if globals.config.getint("BOSWatch","useRegExFilter"): + from includes import filter + if filter.checkFilters(data,typ,pluginName): + logging.debug("call Plugin: %s", pluginName) + plugin.run(typ,freq,data) + logging.debug("return from: %s", pluginName) + + else: logging.debug("call Plugin: %s", pluginName) plugin.run(typ,freq,data) logging.debug("return from: %s", pluginName) - else: - logging.debug("call Plugin: %s", pluginName) - plugin.run(typ,freq,data) - logging.debug("return from: %s", pluginName) - - logging.debug("[END ALARM]") \ No newline at end of file + logging.debug("[END ALARM]") + except: + logging.exception("Error in Alarm processing") \ No newline at end of file diff --git a/includes/decoder.py b/includes/decoder.py index f05fd0b..680f803 100644 --- a/includes/decoder.py +++ b/includes/decoder.py @@ -27,4 +27,4 @@ def decode(freq, decoded): poc.decode(freq, decoded) except: - logging.exception("cannot start decoder") + logging.exception("cannot start decoder") \ No newline at end of file diff --git a/includes/filter.py b/includes/filter.py index 72a2aac..7de49c4 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -9,8 +9,8 @@ from includes import globals # Global variables def loadFilters(): - logging.debug("loading filters") try: + logging.debug("loading filters") for key,val in globals.config.items("Filters"): logging.debug(" - %s = %s", key, val) filter = val.split(";") diff --git a/includes/pluginLoader.py b/includes/pluginLoader.py index ae591cf..2818143 100644 --- a/includes/pluginLoader.py +++ b/includes/pluginLoader.py @@ -24,7 +24,7 @@ def getPlugins(): 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 @@ -36,7 +36,7 @@ def getPlugins(): logging.debug("Plugin [ENABLED ] %s", i) else: logging.debug("Plugin [DISABLED] %s ", i) - except: #no entry for plugin found in config-file, skip + except: #no entry for plugin found in config-file logging.warning("Plugin [NO CONF ] %s", i) except: logging.exception("Error during Plugin search") From c22400420a5cc9d187dc8d95a1d743b1b4495ded Mon Sep 17 00:00:00 2001 From: JHCD Date: Tue, 26 May 2015 20:13:05 +0200 Subject: [PATCH 21/50] add code-documentation to BosMon-plugin --- plugins/BosMon/BosMon.py | 86 +++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/plugins/BosMon/BosMon.py b/plugins/BosMon/BosMon.py index 889dcb0..3c20533 100644 --- a/plugins/BosMon/BosMon.py +++ b/plugins/BosMon/BosMon.py @@ -1,6 +1,16 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +BOSWatch-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to BosMon + +-U {The BosMon hompage} + +@author: Jens Herrmann + +@requires: BosMon-Configuration has to be set in the config.ini +""" + import logging # Global logger import httplib #for the HTTP request @@ -9,8 +19,24 @@ import base64 #for the HTTP request with User/Password from includes import globals # Global variables - +## +# +# do BosMon-Request +# def bosMonRequest(httprequest, params, headers): + """ + Local function to dispatch the BosMon-Request + + @type httprequest: HTTPConnection + @param httprequest: An HTTPConnection-Object that represents an open connection to a BosMon-Instance + @type params: string of urlencoded data + @param params: Contains the parameter for transfer to BosMon. + @type headers: map + @param headers: The headers argument should be a mapping of extra HTTP headers to send with the request. + + @return: nothing + @exception: Exception if httprequest.request failed + """ try: httprequest.request("POST", "/telegramin/"+globals.config.get("BosMon", "bosmon_channel")+"/input.xml", params, headers) except: @@ -22,10 +48,37 @@ def bosMonRequest(httprequest, params, headers): else: logging.warning("BosMon response: %s - %s", str(httpresponse.status), str(httpresponse.reason)) - +## +# +# Main function of BosMon-plugin +# will be called by the alarmHandler +# def run(typ,freq,data): + """ + This function is the implementation of the BosMon-Plugin. + It will send the data to an BosMon-Instance via http + + The configuration for the BosMon-Connection is set in the config.ini. + If an user is set, the HTTP-Request is authenticatet. + + @type typ: string (FMS|ZVEI|POC) + @param typ: Typ of the dataset for sending to BosMon + @type data: map of data (structure see interface.txt) + @param data: Contains the parameter for dispatch to BosMon. + @type freq: string + @keyword freq: frequency is not used in this plugin + + @requires: BosMon-Configuration has to be set in the config.ini + + @return: nothing + @exception: Exception if ConfigParser failed + @exception: Exception if initialize header and connect to BosMon-Server failed + @exception: Exception if urlencoding the params failed + """ try: - #ConfigParser + # + # ConfigParser + # logging.debug("reading config file") try: for key,val in globals.config.items("BosMon"): @@ -34,33 +87,41 @@ def run(typ,freq,data): logging.exception("cannot read config file") try: - #Initialize header an connect to BosMon-Server + # + # Initialize header an connect to BosMon-Server + # headers = {} headers['Content-type'] = "application/x-www-form-urlencoded" headers['Accept'] = "text/plain" + # if an user is set in the config.ini we will use HTTP-Authorization if globals.config.get("BosMon", "bosmon_user"): + # generate b64encoded autorization-token for HTTP-request headers['Authorization'] = "Basic {0}".format(base64.b64encode("{0}:{1}".format(globals.config.get("BosMon", "bosmon_user"), globals.config.get("BosMon", "bosmon_password")))) logging.debug("connect to BosMon") + # open connection to BosMon-Server httprequest = httplib.HTTPConnection(globals.config.get("BosMon", "bosmon_server"), globals.config.get("BosMon", "bosmon_port")) - #debug-level to shell (0=no debug|1) + # debug-level to shell (0=no debug|1) httprequest.set_debuglevel(0) except: logging.exception("cannot connect to BosMon") else: + # + # Format given data-structure to compatible BosMon string + # if typ == "FMS": logging.debug("Start FMS to BosMon") try: - #BosMon-Telegramin expected assembly group, direction and tsi in one field - #structure (binary as hex in base10): + # BosMon-Telegramin expected assembly group, direction and tsi in one field + # structure (binary as hex in base10): # Byte 1: assembly group; Byte 2: Direction; Byte 3+4: tactic short info info = 0 - #assembly group: + # assembly group: info = info + 1 # + b0001 (Assumption: is in every time 1 (no output from multimon-ng)) - #direction: + # direction: if data["direction"] == "1": info = info + 2 # + b0010 - #tsi: + # tsi: if "IV" in data["tsi"]: info = info + 12 # + b1100 elif "III" in data["tsi"]: @@ -71,6 +132,7 @@ def run(typ,freq,data): params = urllib.urlencode({'type':'fms', 'address':data["fms"], 'status':data["status"], 'info':info, 'flags':'0'}) logging.debug(" - Params: %s", params) + # dispatch the BosMon-request bosMonRequest(httprequest, params, headers) except: logging.exception("FMS to BosMon failed") @@ -80,6 +142,7 @@ def run(typ,freq,data): try: params = urllib.urlencode({'type':'zvei', 'address':data["zvei"], 'flags':'0'}) logging.debug(" - Params: %s", params) + # dispatch the BosMon-request bosMonRequest(httprequest, params, headers) except: logging.exception("ZVEI to BosMon failed") @@ -87,10 +150,11 @@ def run(typ,freq,data): elif typ == "POC": logging.debug("Start POC to BosMon") try: - #BosMon-Telegramin expected "a-d" as RIC-sub/function + # BosMon-Telegramin expected "a-d" as RIC-sub/function data["function"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") params = urllib.urlencode({'type':'pocsag', 'address':data["ric"], 'flags':'0', 'function':data["function"], 'message':data["msg"]}) logging.debug(" - Params: %s", params) + # dispatch the BosMon-request bosMonRequest(httprequest, params, headers) except: logging.exception("POC to BosMon failed") From 5394bceedfd058b7316395ae0f312fc91b8fbad8 Mon Sep 17 00:00:00 2001 From: Schrolli Date: Wed, 27 May 2015 07:48:24 +0200 Subject: [PATCH 22/50] insert a Lots of Docu in includes --- includes/alarmHandler.py | 29 ++++++++++++++-- includes/decoder.py | 19 +++++++++++ includes/decoders/fms.py | 21 ++++++++++++ includes/decoders/poc.py | 35 +++++++++++++++++-- includes/decoders/zvei.py | 30 ++++++++++++++++ includes/filter.py | 37 ++++++++++++++++++++ includes/globals.py | 7 ++++ includes/pluginLoader.py | 35 +++++++++++++++++++ includes/shellHeader.py | 72 +++++++++++++++++++++++++-------------- 9 files changed, 255 insertions(+), 30 deletions(-) diff --git a/includes/alarmHandler.py b/includes/alarmHandler.py index 51536a7..bbcc24f 100644 --- a/includes/alarmHandler.py +++ b/includes/alarmHandler.py @@ -1,13 +1,38 @@ #!/usr/bin/python # -*- coding: cp1252 -*- -import logging +""" +Handler for the Filter and Plugins at an Alarm + +@author: Bastian Schroll + +@requires: none +""" + +import logging # Global logger from includes import globals # Global variables + def processAlarm(typ,freq,data): + """ + Function to process Filters and Plugins at Alarm + + @type typ: string (FMS|ZVEI|POC) + @param typ: Typ of the dataset + @type freq: string + @param freq: frequency of the SDR Stick + @type data: map of data (structure see interface.txt) + @param data: Contains the parameter + + @requires: active Plugins in pluginList + + @return: nothing + @exception: Exception if Alarm processing failed + """ try: logging.debug("[ ALARM ]") + #Go to all Plugins in pluginList for pluginName, plugin in globals.pluginList.items(): #if enabled use RegEx-Filter @@ -18,7 +43,7 @@ def processAlarm(typ,freq,data): plugin.run(typ,freq,data) logging.debug("return from: %s", pluginName) - else: + else: #RegEX Filter off - Call Plugin direct logging.debug("call Plugin: %s", pluginName) plugin.run(typ,freq,data) logging.debug("return from: %s", pluginName) diff --git a/includes/decoder.py b/includes/decoder.py index 680f803..0fcfd41 100644 --- a/includes/decoder.py +++ b/includes/decoder.py @@ -1,9 +1,28 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +Search for decode String and call the right decoder Funtion + +@author: Jens Herrmann + +@requires: none +""" + import logging def decode(freq, decoded): + """ + Search for decode String and call the right decoder Function + + @type freq: string + @param freq: frequency of the SDR Stick + @type decoded: string + @param decoded: RAW Information from Multimon-NG + + @return: nothing + @exception: Exception if decoder File call failed + """ try: #FMS Decoder Section #check FMS: -> check CRC -> validate -> check double alarm -> log diff --git a/includes/decoders/fms.py b/includes/decoders/fms.py index 934c7da..7ad12fa 100644 --- a/includes/decoders/fms.py +++ b/includes/decoders/fms.py @@ -1,6 +1,14 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +FMS Decoder + +@author: Bastian Schroll + +@requires: Configuration has to be set in the config.ini +""" + import logging import time #timestamp for doublealarm import re #Regex for validation @@ -10,6 +18,19 @@ from includes import globals # Global variables #FMS Decoder Function #validate -> check double alarm -> log def decode(freq, decoded): + """ + Export FMS Information from Multimon-NG RAW String and call alarmHandler.processAlarm() + + @type freq: string + @param freq: frequency of the SDR Stick + @type decoded: string + @param decoded: RAW Information from Multimon-NG + + @requires: Configuration has to be set in the config.ini + + @return: nothing + @exception: Exception if FMS decode failed + """ timestamp = int(time.time())#Get Timestamp fms_service = decoded[19] #Organisation diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index 593bd95..b58c8ca 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -1,6 +1,15 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +POCSAG Decoder + +@author: Bastian Schroll +@author: Jens Hermann + +@requires: Configuration has to be set in the config.ini +""" + import logging import time #timestamp for doublealarm import re #Regex for validation @@ -9,7 +18,17 @@ from includes import globals # Global variables # Simple Filter def isAllowed(poc_id): - """Simple filter for POCSAG""" + """ + Simple Filter Functions (Allowed ,Denied and Range) + + @type poc_id: string + @param poc_id: POCSAG Ric + + @requires: Configuration has to be set in the config.ini + + @return: True if the Ric is allows, other False + @exception: none + """ # 1.) If allowed RICs is set, only they will path, # If RIC is the right one return True, else False if globals.config.get("POC", "allow_ric"): @@ -36,7 +55,19 @@ def isAllowed(poc_id): #POCSAG Decoder Function #validate -> check double alarm -> log def decode(freq, decoded): - """Decode for structure of typ POCSAG""" + """ + Export POCSAG Information from Multimon-NG RAW String and call alarmHandler.processAlarm() + + @type freq: string + @param freq: frequency of the SDR Stick + @type decoded: string + @param decoded: RAW Information from Multimon-NG + + @requires: Configuration has to be set in the config.ini + + @return: nothing + @exception: Exception if POCSAG decode failed + """ bitrate = 0 timestamp = int(time.time())#Get Timestamp diff --git a/includes/decoders/zvei.py b/includes/decoders/zvei.py index 0c5688e..28f9255 100644 --- a/includes/decoders/zvei.py +++ b/includes/decoders/zvei.py @@ -1,6 +1,14 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +ZVEI Decoder + +@author: Bastian Schroll + +@requires: Configuration has to be set in the config.ini +""" + import logging import time #timestamp for doublealarm import re #Regex for validation @@ -10,6 +18,19 @@ from includes import globals # Global variables #ZVEI Decoder Function #validate -> check double alarm -> log def decode(freq, decoded): + """ + Export ZVEI Information from Multimon-NG RAW String and call alarmHandler.processAlarm() + + @type freq: string + @param freq: frequency of the SDR Stick + @type decoded: string + @param decoded: RAW Information from Multimon-NG + + @requires: Configuration has to be set in the config.ini + + @return: nothing + @exception: Exception if ZVEI decode failed + """ timestamp = int(time.time())#Get Timestamp zvei_id = decoded[7:12] #ZVEI Code @@ -31,6 +52,15 @@ def decode(freq, decoded): def removeF(zvei): + """ + Resolve the F from the repeat Tone + + @type zvei: string + @param zvei: ZVEI Information + + @return: ZVEI without F + @exception: none + """ if "F" in zvei: zvei_old = zvei for i in range(1, 5): diff --git a/includes/filter.py b/includes/filter.py index 7de49c4..63ee3b6 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -1,6 +1,14 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +Functions for the RegEX Filter + +@author: Bastian Schroll + +@requires: Configuration has to be set in the config.ini +""" + import logging # Global logger import re #Regex for Filter Check @@ -9,25 +17,54 @@ from includes import globals # Global variables def loadFilters(): + """ + load all Filters from the config.ini into globals.filterList + + @requires: Configuration has to be set in the config.ini + + @return: nothing + @exception: Exception if Filter loading failed + """ try: logging.debug("loading filters") + #For each entry in config.ini [Filters] Section for key,val in globals.config.items("Filters"): logging.debug(" - %s = %s", key, val) filter = val.split(";") + #insert splitet Data into globals.filterList globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "regex": filter[3]}) except: logging.exception("cannot read config file") def checkFilters(data,typ,plugin): + """ + Check the Typ/Plugin combination with the RegEX Filter + If no Filter for the combination is found, Function returns True. + + @type data: map of data (structure see interface.txt) + @param data: Contains the parameter + @type typ: string (FMS|ZVEI|POC) + @param typ: Typ of the dataset + @type plugin: string + @param plugin: Name of the Plugin to checked + + @requires: all Filters in the filterList + + @return: nothing + @exception: Exception if Filter check failed + """ try: logging.debug("search Filter for %s to %s", typ, plugin) foundFilter = False + #go to all Filter in globals.filterList for i in globals.filterList: + #if Typ/Plugin combination is found if i["typ"] == typ and i["plugin"] == plugin: foundFilter = True logging.debug("found Filter: %s = %s", i["name"], i["regex"]) + #Check the RegEX if re.search(i["regex"], data[i["dataField"]]): logging.debug("Filter passed: %s", i["name"]) return True diff --git a/includes/globals.py b/includes/globals.py index f4af170..1169b29 100644 --- a/includes/globals.py +++ b/includes/globals.py @@ -1,6 +1,13 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +Global variables + +@author: Jens Hermann +@author: Bastian Schroll +""" + #Global variables config = 0 script_path = "" diff --git a/includes/pluginLoader.py b/includes/pluginLoader.py index 2818143..7e7134b 100644 --- a/includes/pluginLoader.py +++ b/includes/pluginLoader.py @@ -1,6 +1,14 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +Functions to Load and import the Plugins + +@author: Bastian Schroll + +@requires: Configuration has to be set in the config.ini +""" + import logging # Global logger import imp import os @@ -8,23 +16,40 @@ import os from includes import globals # Global variables def loadPlugins(): + """ + Load all Plugins into globals.pluginList + + @return: nothing + @exception: Exception if insert into globals.pluginList failed + """ try: logging.debug("loading plugins") + #go to all Plugins from getPlugins() for i in getPlugins(): + #call for each Plugin the loadPlugin() Methode plugin = loadPlugin(i) + #Add it to globals.pluginList globals.pluginList[i["name"]] = plugin except: logging.exception("cannot load Plugins") def getPlugins(): + """ + get a Python Dict of all activeated Plugins + + @return: Plugins as Python Dict + @exception: Exception if Plugin search failed + """ try: logging.debug("Search in Plugin Folder") PluginFolder = globals.script_path+"/plugins" plugins = [] + #Go to all Folders in the Plugin-Dir for i in os.listdir(PluginFolder): location = os.path.join(PluginFolder, i) + #Skip if Path.isdir() or no File DIR_NAME.py is found if not os.path.isdir(location) or not i + ".py" in os.listdir(location): continue @@ -45,6 +70,16 @@ def getPlugins(): def loadPlugin(plugin): + """ + Imports a single Plugin + + @type plugin: Plugin Data + @param plugin: Contains the information to import a Plugin + + + @return: nothing + @exception: Exception if Plugin import failed + """ try: logging.debug("load Plugin: %s", plugin["name"]) return imp.load_module(plugin["name"], *plugin["info"]) diff --git a/includes/shellHeader.py b/includes/shellHeader.py index 4387a6f..7f5e468 100644 --- a/includes/shellHeader.py +++ b/includes/shellHeader.py @@ -1,32 +1,52 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +Shows the Header in Shell if quiet Mode is not active + +@author: Bastian Schroll +@author: Jens Hermann + +@requires: none +""" def printHeader(args): - print " ____ ____ ______ __ __ __ " - print " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ b" - print " / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ e" - print " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / t" - print " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ a" - print " German BOS Information Script " - print " by Bastian Schroll " - print "" + """ + Prints the Header to the Shell - print "Frequency: "+args.freq - print "Device-ID: "+str(args.device) - print "Error in PPM: "+str(args.error) - print "Active Demods: "+str(len(args.demod)) - if "FMS" in args.demod: - print "- FMS" - if "ZVEI" in args.demod: - print "- ZVEI" - if "POC512" in args.demod: - print "- POC512" - if "POC1200" in args.demod: - print "- POC1200" - if "POC2400" in args.demod: - print "- POC2400" - print "Squelch: "+str(args.squelch) - if args.verbose: - print "Verbose Mode!" - print "" \ No newline at end of file + @type args: Array + @param args: All given Arguments from argsparser + + @return: nothing + @exception: Exception if display of the Shell Header failed + """ + try: + print " ____ ____ ______ __ __ __ " + print " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ b" + print " / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ e" + print " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / t" + print " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ a" + print " German BOS Information Script " + print " by Bastian Schroll " + print "" + + print "Frequency: "+args.freq + print "Device-ID: "+str(args.device) + print "Error in PPM: "+str(args.error) + print "Active Demods: "+str(len(args.demod)) + if "FMS" in args.demod: + print "- FMS" + if "ZVEI" in args.demod: + print "- ZVEI" + if "POC512" in args.demod: + print "- POC512" + if "POC1200" in args.demod: + print "- POC1200" + if "POC2400" in args.demod: + print "- POC2400" + print "Squelch: "+str(args.squelch) + if args.verbose: + print "Verbose Mode!" + print "" + except: + logging.exception("cannot display shell header") \ No newline at end of file From 346d5a41f669f7addf1c27e85094c661b8bc9fbc Mon Sep 17 00:00:00 2001 From: Schrolli Date: Wed, 27 May 2015 11:55:04 +0200 Subject: [PATCH 23/50] little changes for docu --- config/config.template.ini | 19 ++++++++++--------- includes/decoders/poc.py | 4 ++-- plugins/interface.txt | 28 ++++++++++++++-------------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/config/config.template.ini b/config/config.template.ini index 8e738e2..5c4c7fa 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -18,7 +18,7 @@ loglevel = 10 backupCount = 7 #Using RegEx-Filter (0|1) -#Filter-configuration in section "Filters" +#Filter-configuration in section [Filters] useRegExFilter = 1 [FMS] @@ -48,15 +48,19 @@ filter_range_end = 9999999 [Filters] #RegEX Filter Configuration +#http://www.regexr.com/ - RegEX Test Tool an Documentation #No Filter for a Typ/Plugin Combination = all Data pass #INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;REGEX #TYP = the Data Typ (FMS|ZVEI|POC) -#DATAFIELD = the field of the Data Array. See interface.txt for names. -#PLUGIN = the name of the Plugin to call with this Filter. +#DATAFIELD = the field of the Data Array (See interface.txt) +#PLUGIN = the name of the Plugin to call with this Filter #REGEX = the RegEX #only ZVEI to template with 25### -#testfilter = ZVEI;zvei;template;25[0-9F]{3} +#testfilter = ZVEI;zvei;template;25[0-9]{3} + +#only POCSAG to MySQL with the text "ALARM:" in the Message +#pocTest = POC;msg;MySQL;ALARM: [Plugins] @@ -106,9 +110,6 @@ poc_url = www.google.de ##### Not ready yet # ##################### - [template] -data1 = test123 -data2 = test345 -data3 = test567 -data4 = test789 \ No newline at end of file +test1 = testString +test2 = 123456 \ No newline at end of file diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index b58c8ca..c879246 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -19,14 +19,14 @@ from includes import globals # Global variables # Simple Filter def isAllowed(poc_id): """ - Simple Filter Functions (Allowed ,Denied and Range) + Simple Filter Functions (Allowed, Denied and Range) @type poc_id: string @param poc_id: POCSAG Ric @requires: Configuration has to be set in the config.ini - @return: True if the Ric is allows, other False + @return: True if the Ric is allowed, other False @exception: none """ # 1.) If allowed RICs is set, only they will path, diff --git a/plugins/interface.txt b/plugins/interface.txt index 016f079..3000604 100644 --- a/plugins/interface.txt +++ b/plugins/interface.txt @@ -1,12 +1,11 @@ -Übergabe an Plugin: - -typ = [FMS|ZVEI|POC] -freq = [FREQ(Hz)] -data = {"KEY1":"VALUE1","KEY2":"VALUE2"} als Python Dict +Handover to Plugin: +typ = [FMS|ZVEI|POC] +freq = [Freq in Hz] +data = {"KEY1":"VALUE1","KEY2":"VALUE2"} -Follgende Daten sind bei der jeweiligen Alarm-Art enthalten -und per data["OPTION"] abrufbar: +The following informations are included in the var "data". +They can be used by their Index Names: data['OPTION'] ZVEI: - zvei @@ -24,13 +23,14 @@ POCSAG: - bitrate -Es stehen folgende globale Objecte zur Verfügung: +Global Objects: -1.) import logging # Global logger -logging - Object -Nachricht ins Log per: logging.LOGLEVEL("MESSAGE") -Mögliche Loglevel: debug|info|warning|error|exception|critical +1.) +import logging # Global logger +Message into Log: logging.LOGLEVEL("MESSAGE") +Loglevel: debug|info|warning|error|exception|critical -2.) import globals # Global variables -config - Object (typ: ConfigParser, stellt config.ini bereit) +2.) +import globals # Global variables +reads Data from the config.ini VALUE = globals.config.get("SECTION", "OPTION") \ No newline at end of file From 8c97973417c82f7614848d1ca957504973da45c8 Mon Sep 17 00:00:00 2001 From: Schrolli Date: Wed, 27 May 2015 12:48:53 +0200 Subject: [PATCH 24/50] edit plugin_test.py --- plugin_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin_test.py b/plugin_test.py index 624ca8b..ce6a67a 100644 --- a/plugin_test.py +++ b/plugin_test.py @@ -53,7 +53,7 @@ except: pluginLoader.loadPlugins() -filter.getFilters() +filter.loadFilters() # ----- Test Data ----- # @@ -61,7 +61,7 @@ filter.getFilters() #data = {"fms":"12345678", "status":"2", "direction":"1", "tsi":"III"} typ = "ZVEI" -data = {"zvei":"12345"} +data = {"zvei":"25345"} #typ = "POC" #data = {"ric":"1234567", "function":"1", "msg":"Hello World!, "bitrate":"1200"} From 475039c171ef2709474fbb648c34f58e51faa5a2 Mon Sep 17 00:00:00 2001 From: Schrolli Date: Thu, 28 May 2015 08:43:08 +0200 Subject: [PATCH 25/50] little changes --- boswatch.py | 2 +- config/config.template.ini | 25 +++++++++-- plugins/httpRequest/httpRequest.py | 67 ++++++++++++++++-------------- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/boswatch.py b/boswatch.py index 5eb60f6..97e6063 100755 --- a/boswatch.py +++ b/boswatch.py @@ -130,7 +130,7 @@ try: logging.debug(" - Demod: POC512") if "POC1200" in args.demod: demodulation += "-a POCSAG1200 " - logging.debug(" - Demod: P") + logging.debug(" - Demod: POC1200") if "POC2400" in args.demod: demodulation += "-a POCSAG2400 " logging.debug(" - Demod: POC2400") diff --git a/config/config.template.ini b/config/config.template.ini index 5c4c7fa..f26d802 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -101,9 +101,28 @@ bosmon_password = [httpRequest] #URL without http:// -fms_url = www.google.de -zvei_url = www.google.de -poc_url = www.google.de + +### !!! WILDCARDS actually not implemented !!! ### +#you can use the following wildcards in your URL as GET params: +#http://en.wikipedia.org/wiki/Query_string + +# %FMS% = FMS Code +# %STATUS% = FMS Status +# %DIR% = Direction of the telegram +# %TSI% = Tactical Short Information +#fms_url = www.google.de?code=%FMS%&stat=%STATUS% +fms_url = + +# %ZVEI% = ZVEI 5-tone Code +#zvei_url = www.google.de?zvei=%ZVEI% +zvei_url = + +# %ric% = Pocsag RIC +# %func% = Pocsac fcuntion/Subric +# %msg% = Message of the Pocsag telegram +# %bitrate% = Bitrate of the Pocsag telegram +#poc_url = www.google.de?ric=%RIC%&subric=%FUNC%&msg=%MSG% +poc_url = ##################### diff --git a/plugins/httpRequest/httpRequest.py b/plugins/httpRequest/httpRequest.py index 85ee16f..0fb3a86 100644 --- a/plugins/httpRequest/httpRequest.py +++ b/plugins/httpRequest/httpRequest.py @@ -1,32 +1,40 @@ #!/usr/bin/python # -*- coding: cp1252 -*- -######### -# USAGE -# -# Config -# ====== -# to read a option from config File -# VALUE = globals.config.get("SECTION", "OPTION") -# -# Data from boswatch.py -# ===================== -# use data["KEY"] for Alarm Data from boswatch.py -# for usable KEYs in different Functions (FMS|ZVEI|POC) see interface.txt -# -# LOG Messages -# ============ -# send Log Messages with logging.LOGLEVEL("MESSAGE") -# usable Loglevels debug|info|warning|error|exception|critical -# if you use .exception in Try:Exception: Construct, it logs the Python EX.message too +""" +httpRequest-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to an URL + +@author: Bastian Schroll + +@requires: httpRequest-Configuration has to be set in the config.ini +""" import logging # Global logger import httplib #for the HTTP request +from urlparse import urlparse #for split the URL into url and path from includes import globals # Global variables def run(typ,freq,data): + """ + This function is the implementation of the httpRequest-Plugin. + It will send the data to an URL via http Request + + @type typ: string (FMS|ZVEI|POC) + @param typ: Typ of the dataset + @type data: map of data (structure see interface.txt) + @param data: Contains the parameter + @type freq: string + @keyword freq: frequency of the SDR Stick + + @requires: httpRequest-Configuration has to be set in the config.ini + + @return: nothing + @exception: Exception if ConfigParser failed + @exception: Exception if http Request failed + @exception: Exception if http Response failed + """ try: #ConfigParser logging.debug("reading config file") @@ -36,41 +44,38 @@ def run(typ,freq,data): except: logging.exception("cannot read config file") -########## 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", "/") + url = globals.config.get("httpRequest", "fms_url") elif typ == "ZVEI": - httprequest = httplib.HTTPConnection(globals.config.get("httpRequest", "zvei_url")) - httprequest.request("HEAD", "/") + url = globals.config.get("httpRequest", "zvei_url") elif typ == "POC": - httprequest = httplib.HTTPConnection(globals.config.get("httpRequest", "poc_url")) - httprequest.request("HEAD", "/") + url = globals.config.get("httpRequest", "poc_url") else: logging.warning("Invalid Typ: %s", typ) - + + httprequest = httplib.HTTPConnection(url) + httprequest.request("HEAD", path) + except: logging.exception("cannot send HTTP request") else: try: - httpresponse = httprequest.getresponse() + 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") finally: logging.debug("close HTTP-Connection") httprequest.close() -########## User Plugin CODE ########## - + except: logging.exception("unknown error") \ No newline at end of file From 9bfe832530c859b754570649a6be5fd627bf4b5a Mon Sep 17 00:00:00 2001 From: Schrolli Date: Thu, 28 May 2015 09:17:22 +0200 Subject: [PATCH 26/50] fix error in httpRequest plugin --- plugins/httpRequest/httpRequest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/httpRequest/httpRequest.py b/plugins/httpRequest/httpRequest.py index 0fb3a86..cb552dc 100644 --- a/plugins/httpRequest/httpRequest.py +++ b/plugins/httpRequest/httpRequest.py @@ -58,7 +58,7 @@ def run(typ,freq,data): logging.warning("Invalid Typ: %s", typ) httprequest = httplib.HTTPConnection(url) - httprequest.request("HEAD", path) + httprequest.request("HEAD", url) except: logging.exception("cannot send HTTP request") From b3ee8d29aedc7211871946bc3036de657c48f569 Mon Sep 17 00:00:00 2001 From: Schrolli Date: Thu, 28 May 2015 09:18:21 +0200 Subject: [PATCH 27/50] checkFilters() with freq und wildcards --- README.md | 24 +++++++++++++++++------- config/config.template.ini | 15 ++++++++------- includes/alarmHandler.py | 2 +- includes/filter.py | 18 ++++++++++-------- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index bad33ae..823c018 100644 --- a/README.md +++ b/README.md @@ -48,13 +48,23 @@ For each Plugin that requires configurations a own Section with his Name is avai For the other Functions see "Usage" below. -##### Filtering Functions -For the Filter Functions see Section `[Filters]` -Syntax: INDIVIDUAL_NAME = TYP;PLUGIN;REGEX -TYP = FMS|ZVEI|POC -PLUGIN = Name of the Plugin -REGEX = Filtering with RegEx -my_own_Filter = ZVEI;template;25[0-9F]{3} #all ZVEI alarm to Plugin template where 25### +##### Filtering Functions (RegEX) +For the RegEX Filter Functions see Section `[Filters]` +http://www.regexr.com/ - RegEX Test Tool an Documentation +No Filter for a Typ/Plugin Combination = all Data pass + +Syntax: INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX +- TYP = the Data Typ (FMS|ZVEI|POC) +- DATAFIELD = the field of the Data Array (See interface.txt) +- PLUGIN = the name of the Plugin to call with this Filter (* for all) +- FREQUENZ = the Frequenz to use the Filter (for more SDR Sticks (* for all)) +- REGEX = the RegEX + +only ZVEI to all Plugins with 25### at 85.5MHz +testfilter = ZVEI;zvei;*;85500000;25[0-9]{3} + +only POCSAG to MySQL with the text "ALARM:" in the Message +pocTest = POC;msg;MySQL;*;ALARM: ##### Web Frontend Put the Files in Folder /wwww/ into your local Webserver Folder (/var/www/). diff --git a/config/config.template.ini b/config/config.template.ini index f26d802..e7e07bb 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -50,17 +50,18 @@ filter_range_end = 9999999 #RegEX Filter Configuration #http://www.regexr.com/ - RegEX Test Tool an Documentation #No Filter for a Typ/Plugin Combination = all Data pass -#INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;REGEX -#TYP = the Data Typ (FMS|ZVEI|POC) +#INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX +#TYP = the Data Typ (FMS|ZVEI|POC) #DATAFIELD = the field of the Data Array (See interface.txt) -#PLUGIN = the name of the Plugin to call with this Filter -#REGEX = the RegEX +#PLUGIN = the name of the Plugin to call with this Filter (* for all) +#FREQUENZ = the Frequenz to use the Filter (for more SDR Sticks (* for all)) +#REGEX = the RegEX -#only ZVEI to template with 25### -#testfilter = ZVEI;zvei;template;25[0-9]{3} +#only ZVEI to all Plugins with 25### at 85.5MHz +#testfilter = ZVEI;zvei;*;85500000;25[0-9]{3} #only POCSAG to MySQL with the text "ALARM:" in the Message -#pocTest = POC;msg;MySQL;ALARM: +#pocTest = POC;msg;MySQL;*;ALARM: [Plugins] diff --git a/includes/alarmHandler.py b/includes/alarmHandler.py index bbcc24f..c5d75ea 100644 --- a/includes/alarmHandler.py +++ b/includes/alarmHandler.py @@ -38,7 +38,7 @@ def processAlarm(typ,freq,data): #if enabled use RegEx-Filter if globals.config.getint("BOSWatch","useRegExFilter"): from includes import filter - if filter.checkFilters(data,typ,pluginName): + if filter.checkFilters(typ,data,pluginName,freq): logging.debug("call Plugin: %s", pluginName) plugin.run(typ,freq,data) logging.debug("return from: %s", pluginName) diff --git a/includes/filter.py b/includes/filter.py index 63ee3b6..52745b0 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -32,22 +32,24 @@ def loadFilters(): logging.debug(" - %s = %s", key, val) filter = val.split(";") #insert splitet Data into globals.filterList - globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "regex": filter[3]}) + globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "freq": filter[3], "regex": filter[4]}) except: logging.exception("cannot read config file") -def checkFilters(data,typ,plugin): +def checkFilters(typ,data,plugin,freq): """ Check the Typ/Plugin combination with the RegEX Filter If no Filter for the combination is found, Function returns True. - - @type data: map of data (structure see interface.txt) - @param data: Contains the parameter + @type typ: string (FMS|ZVEI|POC) @param typ: Typ of the dataset + @type data: map of data (structure see interface.txt) + @param data: Contains the parameter @type plugin: string @param plugin: Name of the Plugin to checked + @type freq: string + @param freq: frequency of the SDR Stick @requires: all Filters in the filterList @@ -55,13 +57,13 @@ def checkFilters(data,typ,plugin): @exception: Exception if Filter check failed """ try: - logging.debug("search Filter for %s to %s", typ, plugin) + logging.debug("search Filter for %s to %s at %s Hz", typ, plugin, freq) foundFilter = False #go to all Filter in globals.filterList for i in globals.filterList: - #if Typ/Plugin combination is found - if i["typ"] == typ and i["plugin"] == plugin: + #if Typ/Plugin/Freq combination is found + if i["typ"] == typ and (i["plugin"] == plugin or i['plugin'] == "*") and (i["freq"] == freq or i['freq'] == "*"): foundFilter = True logging.debug("found Filter: %s = %s", i["name"], i["regex"]) #Check the RegEX From 5454e4980f17fd31e0ff660ddb25bccf1b4b239b Mon Sep 17 00:00:00 2001 From: Schrolli Date: Thu, 28 May 2015 13:26:00 +0200 Subject: [PATCH 28/50] little change --- plugins/httpRequest/httpRequest.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/httpRequest/httpRequest.py b/plugins/httpRequest/httpRequest.py index cb552dc..7ea6d20 100644 --- a/plugins/httpRequest/httpRequest.py +++ b/plugins/httpRequest/httpRequest.py @@ -57,8 +57,10 @@ def run(typ,freq,data): else: logging.warning("Invalid Typ: %s", typ) - httprequest = httplib.HTTPConnection(url) - httprequest.request("HEAD", url) + url_path = urlparse(url)#get the URL path + + httprequest = httplib.HTTPConnection(url_path[2]) + httprequest.request("GET", url) except: logging.exception("cannot send HTTP request") From a0521a6b368e46a53ff1186c423b21c58686aeae Mon Sep 17 00:00:00 2001 From: Schrolli Date: Thu, 28 May 2015 13:39:53 +0200 Subject: [PATCH 29/50] httpRequest Plugin with wildcard params --- README.md | 5 +++-- config/config.template.ini | 9 ++++----- plugins/httpRequest/httpRequest.py | 19 +++++++++++-------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 823c018..c721dfe 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ unless you are developer you can use the develop-Branch - may be unstable! ##### Implemented Plugins: - MySQL (insert Data into MySQL Database [FMS|ZVEI|POC]) - BosMon (send Data to BosMon Server [FMS|ZVEI|POC]) -- httpRequest (send a request to an URL [FMS|ZVEI|POC]) +- httpRequest (send a request with parameter to an URL [FMS|ZVEI|POC]) +for more Information to the Plugins see `config.ini` ##### Plugins for the Future: - E-mail Notification @@ -53,7 +54,7 @@ For the RegEX Filter Functions see Section `[Filters]` http://www.regexr.com/ - RegEX Test Tool an Documentation No Filter for a Typ/Plugin Combination = all Data pass -Syntax: INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX +Syntax: INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX (separator ";") - TYP = the Data Typ (FMS|ZVEI|POC) - DATAFIELD = the field of the Data Array (See interface.txt) - PLUGIN = the name of the Plugin to call with this Filter (* for all) diff --git a/config/config.template.ini b/config/config.template.ini index e7e07bb..99d9924 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -103,7 +103,6 @@ bosmon_password = [httpRequest] #URL without http:// -### !!! WILDCARDS actually not implemented !!! ### #you can use the following wildcards in your URL as GET params: #http://en.wikipedia.org/wiki/Query_string @@ -118,10 +117,10 @@ fms_url = #zvei_url = www.google.de?zvei=%ZVEI% zvei_url = -# %ric% = Pocsag RIC -# %func% = Pocsac fcuntion/Subric -# %msg% = Message of the Pocsag telegram -# %bitrate% = Bitrate of the Pocsag telegram +# %RIC% = Pocsag RIC +# %FUNC% = Pocsac fcuntion/Subric +# %MSG% = Message of the Pocsag telegram +# %BITRATE% = Bitrate of the Pocsag telegram #poc_url = www.google.de?ric=%RIC%&subric=%FUNC%&msg=%MSG% poc_url = diff --git a/plugins/httpRequest/httpRequest.py b/plugins/httpRequest/httpRequest.py index 7ea6d20..b36f21a 100644 --- a/plugins/httpRequest/httpRequest.py +++ b/plugins/httpRequest/httpRequest.py @@ -43,24 +43,27 @@ def run(typ,freq,data): logging.debug(" - %s = %s", key, val) except: logging.exception("cannot read config file") - try: logging.debug("send %s HTTP request", typ) if typ == "FMS": - url = globals.config.get("httpRequest", "fms_url") + url = globals.config.get("httpRequest", "fms_url") #Get URL + url = url.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards in URL + url = url.replace("%DIR%", data["direction"]).replace("%TSI%", data["tsi"]) #replace Wildcards in URL elif typ == "ZVEI": - url = globals.config.get("httpRequest", "zvei_url") + url = globals.config.get("httpRequest", "zvei_url") #Get URL + url = url.replace("%ZVEI%", data["zvei"]) #replace Wildcards in URL elif typ == "POC": - url = globals.config.get("httpRequest", "poc_url") + url = globals.config.get("httpRequest", "poc_url") #Get URL + url = url.replace("%RIC%", data["ric"]).replace("%FUNC%", data["function"]) #replace Wildcards in URL + url = url.replace("%MSG%", data["msg"]).replace("%BITRATE%", data["bitrate"]) #replace Wildcards in URL else: logging.warning("Invalid Typ: %s", typ) - url_path = urlparse(url)#get the URL path - - httprequest = httplib.HTTPConnection(url_path[2]) - httprequest.request("GET", url) + url = urlparse(url)#split URL into path and querry + httprequest = httplib.HTTPConnection(url[2]) #connect to URL Path + httprequest.request("GET", url[5]) #send URL Querry per GET except: logging.exception("cannot send HTTP request") From 29a21e1cbbf6d05bf8e5f70ca0c243c44d90ffd2 Mon Sep 17 00:00:00 2001 From: Schrolli Date: Thu, 28 May 2015 13:51:20 +0200 Subject: [PATCH 30/50] edit boswatch.py File Header --- boswatch.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/boswatch.py b/boswatch.py index 97e6063..017df48 100755 --- a/boswatch.py +++ b/boswatch.py @@ -1,14 +1,16 @@ #!/usr/bin/python # -*- coding: cp1252 -*- # -# BOSWatch -# Autor: Bastian Schroll -# -# - """ +BOSWatch Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG +Through a simple plugin system, data can easily be transferred to other applications For more Information see the README.md + +Autor: Bastian Schroll +Autor: Jens Hermann + +GitHUB: https://github.com/Schrolli91/BOSWatch """ import logging From 2e91c3d8c952be15842d5d8b278210c94ba24266 Mon Sep 17 00:00:00 2001 From: Schrolli Date: Fri, 29 May 2015 11:25:25 +0200 Subject: [PATCH 31/50] insert freqToHz() method in boswatch.py --- boswatch.py | 24 +++++++++++++++++++++--- includes/filter.py | 2 +- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/boswatch.py b/boswatch.py index 017df48..972f192 100755 --- a/boswatch.py +++ b/boswatch.py @@ -33,6 +33,24 @@ class MyTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): """Set/Change backupCount""" self.backupCount = backupCount + +def freqToHz(freq) + """ + gets a Frequenz and resolve it in Hz + + @type freq: string + @param freq: frequency of the SDR Stick + + @return: Frequenz in Hz + @exception: Exception if Error by recalc + """ + try: + freq = freq.replace("k","e3").replace("M","e6") + return int(freq) + except: + logging.exception("Error in freqToHz()") + + # # Programm # @@ -115,7 +133,7 @@ try: # # For debug display/log args # - logging.debug(" - Frequency: %s", args.freq) + logging.debug(" - Frequency: %s", freqToHz(args.freq)) logging.debug(" - Device: %s", args.device) logging.debug(" - PPM Error: %s", args.error) logging.debug(" - Squelch: %s", args.squelch) @@ -193,7 +211,7 @@ try: # Start rtl_fm # logging.debug("starting rtl_fm") - rtl_fm = subprocess.Popen("rtl_fm -d "+str(args.device)+" -f "+str(args.freq)+" -M fm -s 22050 -p "+str(args.error)+" -E DC -F 0 -l "+str(args.squelch)+" -g 100", + rtl_fm = subprocess.Popen("rtl_fm -d "+str(args.device)+" -f "+str(freqToHz(args.freq))+" -M fm -s 22050 -p "+str(args.error)+" -E DC -F 0 -l "+str(args.squelch)+" -g 100", #stdin=rtl_fm.stdout, stdout=subprocess.PIPE, stderr=open(globals.script_path+"/log/rtl_fm.log","a"), @@ -240,7 +258,7 @@ try: #time.sleep(1) from includes import decoder - decoder.decode(args.freq, decoded) + decoder.decode(freqToHz(args.freq), decoded) except KeyboardInterrupt: logging.warning("Keyboard Interrupt") diff --git a/includes/filter.py b/includes/filter.py index 52745b0..ca72a5f 100644 --- a/includes/filter.py +++ b/includes/filter.py @@ -32,7 +32,7 @@ def loadFilters(): logging.debug(" - %s = %s", key, val) filter = val.split(";") #insert splitet Data into globals.filterList - globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "freq": filter[3], "regex": filter[4]}) + globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "freq": freqToHz(filter[3]), "regex": filter[4]}) except: logging.exception("cannot read config file") From 1ee27afbf040b4418f6ab6a8902f2c748175315d Mon Sep 17 00:00:00 2001 From: JHCD Date: Sun, 31 May 2015 12:38:35 +0200 Subject: [PATCH 32/50] move boswatch.sql --- boswatch.sql => sql/boswatch.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename boswatch.sql => sql/boswatch.sql (100%) diff --git a/boswatch.sql b/sql/boswatch.sql similarity index 100% rename from boswatch.sql rename to sql/boswatch.sql From 0a621b283a8e7fba3a373fa359588b8ae7c60e60 Mon Sep 17 00:00:00 2001 From: JHCD Date: Sun, 31 May 2015 12:40:43 +0200 Subject: [PATCH 33/50] a little bit documentation --- boswatch.py | 6 ++-- config/config.template.ini | 4 +-- includes/decoders/fms.py | 7 +++-- includes/decoders/poc.py | 13 ++++++--- includes/decoders/zvei.py | 7 +++-- includes/globals.py | 2 +- includes/shellHeader.py | 2 +- plugins/BosMon/BosMon.py | 8 +++++- plugins/MySQL/MySQL.py | 45 +++++++++++++++++++++++++++++- plugins/httpRequest/httpRequest.py | 23 ++++++++++++--- 10 files changed, 97 insertions(+), 20 deletions(-) diff --git a/boswatch.py b/boswatch.py index 972f192..33dbc65 100755 --- a/boswatch.py +++ b/boswatch.py @@ -7,8 +7,8 @@ Python Script to receive and decode German BOS Information with rtl_fm and multi Through a simple plugin system, data can easily be transferred to other applications For more Information see the README.md -Autor: Bastian Schroll -Autor: Jens Hermann +@author: Bastian Schroll +@author: Jens Herrmann GitHUB: https://github.com/Schrolli91/BOSWatch """ @@ -25,7 +25,9 @@ import subprocess from includes import globals # Global variables ## +# # This Class extended the TimedRotatingFileHandler with the possibility to change the backupCount after initialization. +# ## class MyTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): """Extended Version of TimedRotatingFileHandler""" diff --git a/config/config.template.ini b/config/config.template.ini index 99d9924..065650f 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -19,7 +19,7 @@ backupCount = 7 #Using RegEx-Filter (0|1) #Filter-configuration in section [Filters] -useRegExFilter = 1 +useRegExFilter = 0 [FMS] #time to ignore same alarm in a row (sek) @@ -118,7 +118,7 @@ fms_url = zvei_url = # %RIC% = Pocsag RIC -# %FUNC% = Pocsac fcuntion/Subric +# %FUNC% = Pocsac function/Subric # %MSG% = Message of the Pocsag telegram # %BITRATE% = Bitrate of the Pocsag telegram #poc_url = www.google.de?ric=%RIC%&subric=%FUNC%&msg=%MSG% diff --git a/includes/decoders/fms.py b/includes/decoders/fms.py index 7ad12fa..1f7dfa1 100644 --- a/includes/decoders/fms.py +++ b/includes/decoders/fms.py @@ -15,8 +15,11 @@ import re #Regex for validation from includes import globals # Global variables -#FMS Decoder Function -#validate -> check double alarm -> log +## +# +# FMS Decoder Function +# validate -> check double alarm -> log +# def decode(freq, decoded): """ Export FMS Information from Multimon-NG RAW String and call alarmHandler.processAlarm() diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index c879246..f4fd313 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -5,7 +5,7 @@ POCSAG Decoder @author: Bastian Schroll -@author: Jens Hermann +@author: Jens Herrmann @requires: Configuration has to be set in the config.ini """ @@ -16,7 +16,10 @@ import re #Regex for validation from includes import globals # Global variables +## +# # Simple Filter +# def isAllowed(poc_id): """ Simple Filter Functions (Allowed, Denied and Range) @@ -51,9 +54,11 @@ def isAllowed(poc_id): return False return True - -#POCSAG Decoder Function -#validate -> check double alarm -> log +## +# +# POCSAG Decoder Function +# validate -> check double alarm -> log +# def decode(freq, decoded): """ Export POCSAG Information from Multimon-NG RAW String and call alarmHandler.processAlarm() diff --git a/includes/decoders/zvei.py b/includes/decoders/zvei.py index 28f9255..1d87f12 100644 --- a/includes/decoders/zvei.py +++ b/includes/decoders/zvei.py @@ -15,8 +15,11 @@ import re #Regex for validation from includes import globals # Global variables -#ZVEI Decoder Function -#validate -> check double alarm -> log +## +# +# ZVEI Decoder Function +# validate -> check double alarm -> log +# def decode(freq, decoded): """ Export ZVEI Information from Multimon-NG RAW String and call alarmHandler.processAlarm() diff --git a/includes/globals.py b/includes/globals.py index 1169b29..b96e81b 100644 --- a/includes/globals.py +++ b/includes/globals.py @@ -4,7 +4,7 @@ """ Global variables -@author: Jens Hermann +@author: Jens Herrmann @author: Bastian Schroll """ diff --git a/includes/shellHeader.py b/includes/shellHeader.py index 7f5e468..b1bcb52 100644 --- a/includes/shellHeader.py +++ b/includes/shellHeader.py @@ -5,7 +5,7 @@ Shows the Header in Shell if quiet Mode is not active @author: Bastian Schroll -@author: Jens Hermann +@author: Jens Herrmann @requires: none """ diff --git a/plugins/BosMon/BosMon.py b/plugins/BosMon/BosMon.py index 3c20533..e91351a 100644 --- a/plugins/BosMon/BosMon.py +++ b/plugins/BosMon/BosMon.py @@ -25,7 +25,7 @@ from includes import globals # Global variables # def bosMonRequest(httprequest, params, headers): """ - Local function to dispatch the BosMon-Request + This local function dispatch the BosMon-Request @type httprequest: HTTPConnection @param httprequest: An HTTPConnection-Object that represents an open connection to a BosMon-Instance @@ -38,10 +38,16 @@ def bosMonRequest(httprequest, params, headers): @exception: Exception if httprequest.request failed """ try: + # + # BosMon/HTTP-Request + # httprequest.request("POST", "/telegramin/"+globals.config.get("BosMon", "bosmon_channel")+"/input.xml", params, headers) except: logging.exception("request to BosMon failed") else: + # + # check HTTP-Response + # httpresponse = httprequest.getresponse() if str(httpresponse.status) == "200": #Check HTTP Response an print a Log or Error logging.debug("BosMon response: %s - %s", str(httpresponse.status), str(httpresponse.reason)) diff --git a/plugins/MySQL/MySQL.py b/plugins/MySQL/MySQL.py index be19b3e..887d314 100644 --- a/plugins/MySQL/MySQL.py +++ b/plugins/MySQL/MySQL.py @@ -1,6 +1,16 @@ #!/usr/bin/python # -*- coding: cp1252 -*- +""" +MySQL-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to a MySQL database + +@author: Jens Herrmann +@author: Bastian Schroll + +@requires: MySQL-Configuration has to be set in the config.ini +@requires: Created Database/Tables, see boswatch.sql +""" + import logging # Global logger import mysql @@ -9,7 +19,35 @@ import mysql.connector from includes import globals # Global variables +## +# +# Main function of MySQL-plugin +# will be called by the alarmHandler +# def run(typ,freq,data): + """ + This function is the implementation of the MySQL-Plugin. + It will store the data to an MySQL database + + The configuration for the MySQL-Connection is set in the config.ini. + For DB- and tablestructure see boswatch.sql + + @type typ: string (FMS|ZVEI|POC) + @param typ: Typ of the dataset for sending to BosMon + @type data: map of data (structure see interface.txt) + @param data: Contains the parameter for dispatch to BosMon. + @type freq: string + @keyword freq: frequency is not used in this plugin + + @requires: MySQL-Configuration has to be set in the config.ini + @requires: Created Database/Tables, see boswatch.sql + + @return: nothing + @exception: Exception if ConfigParser failed + @exception: Exception if connect to MySQL failed + @exception: Exception if executing the sql-statement is failed + """ + try: #ConfigParser logging.debug("reading config file") @@ -20,14 +58,19 @@ def run(typ,freq,data): logging.exception("cannot read config file") try: + # + # Connect to MySQL + # logging.debug("connect to MySQL") connection = mysql.connector.connect(host = globals.config.get("MySQL","dbserver"), user = globals.config.get("MySQL","dbuser"), passwd = globals.config.get("MySQL","dbpassword"), db = globals.config.get("MySQL","database")) cursor = connection.cursor() except: logging.exception("cannot connect to MySQL") else: - try: + # + # Create and execute SQL-statement + # logging.debug("Insert %s", typ) if typ == "FMS": diff --git a/plugins/httpRequest/httpRequest.py b/plugins/httpRequest/httpRequest.py index b36f21a..da48450 100644 --- a/plugins/httpRequest/httpRequest.py +++ b/plugins/httpRequest/httpRequest.py @@ -16,6 +16,11 @@ from urlparse import urlparse #for split the URL into url and path from includes import globals # Global variables +## +# +# Main function of HTTP-plugin +# will be called by the alarmHandler +# def run(typ,freq,data): """ This function is the implementation of the httpRequest-Plugin. @@ -36,7 +41,9 @@ def run(typ,freq,data): @exception: Exception if http Response failed """ try: - #ConfigParser + # + # ConfigParser + # logging.debug("reading config file") try: for key,val in globals.config.items("httpRequest"): @@ -45,6 +52,9 @@ def run(typ,freq,data): logging.exception("cannot read config file") try: + # + # Create URL + # logging.debug("send %s HTTP request", typ) if typ == "FMS": @@ -61,15 +71,20 @@ def run(typ,freq,data): else: logging.warning("Invalid Typ: %s", typ) - url = urlparse(url)#split URL into path and querry + # + # HTTP-Request + # + url = urlparse(url) #split URL into path and querry httprequest = httplib.HTTPConnection(url[2]) #connect to URL Path httprequest.request("GET", url[5]) #send URL Querry per GET except: logging.exception("cannot send HTTP request") else: - - try: + try: + # + # check HTTP-Response + # 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)) From 2462596232941849343d7ff1dd3b68c0efc7a652 Mon Sep 17 00:00:00 2001 From: JHCD Date: Sun, 31 May 2015 13:12:37 +0200 Subject: [PATCH 34/50] fix function freqToHz() for linux --- boswatch.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/boswatch.py b/boswatch.py index 33dbc65..da818fa 100755 --- a/boswatch.py +++ b/boswatch.py @@ -36,19 +36,25 @@ class MyTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): self.backupCount = backupCount -def freqToHz(freq) +## +# +# convert frequency to Hz +# +def freqToHz(freq): """ - gets a Frequenz and resolve it in Hz + gets a frequency and resolve it in Hz @type freq: string @param freq: frequency of the SDR Stick - @return: Frequenz in Hz + @return: frequency in Hz @exception: Exception if Error by recalc """ try: freq = freq.replace("k","e3").replace("M","e6") - return int(freq) + # freq has to be interpreted as float first... + # otherwise you will get the error: an invalid literal for int() with base 10 + return int(float(freq)) except: logging.exception("Error in freqToHz()") From 9b590b6b73329bbb6527a63bcbac73ff29b2e1b8 Mon Sep 17 00:00:00 2001 From: Smith-fms Date: Wed, 3 Jun 2015 18:05:29 +0200 Subject: [PATCH 35/50] added POCSAG Support --- www/tpl/content.overview.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/www/tpl/content.overview.php b/www/tpl/content.overview.php index 8d2f141..8f4f7f6 100644 --- a/www/tpl/content.overview.php +++ b/www/tpl/content.overview.php @@ -16,4 +16,12 @@ $Rows[] = $daten; } $tpl['zvei'] = $Rows; -?> \ No newline at end of file + //read POCSAG + $db->query("SELECT id, time, ric, funktion, text FROM ".$tablePOC." ORDER BY id DESC LIMIT 50"); + $Rows = array(); + while ($daten = $db->fetchAssoc()) + { + $Rows[] = $daten; + } + $tpl['poc'] = $Rows; +?> From bdf670bd4616e4540c65e50d87f1b3d69cd81563 Mon Sep 17 00:00:00 2001 From: Smith-fms Date: Wed, 3 Jun 2015 18:06:59 +0200 Subject: [PATCH 36/50] added POCSAG Support --- www/tpl/template.overview.php | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/www/tpl/template.overview.php b/www/tpl/template.overview.php index e1c973a..6fd6e2a 100644 --- a/www/tpl/template.overview.php +++ b/www/tpl/template.overview.php @@ -28,6 +28,33 @@ Last alarms for FMS and ZVEI (max. 50)

echo ""; } ?> + +
+ Last POCSAG alarms + + + + + + + + + "; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + ?>
IDDatum - ZeitRICFunktionText
". $poc['id'] . "". $time . "". $poc['ric'] . "". $poc['funktion'] . "". $poc['text'] . "
@@ -54,4 +81,4 @@ Last alarms for FMS and ZVEI (max. 50)

} ?> - \ No newline at end of file + From 70094d55a3c1034cfe73678d8c44ab2e7bb6df05 Mon Sep 17 00:00:00 2001 From: JHCD Date: Thu, 4 Jun 2015 19:03:32 +0200 Subject: [PATCH 37/50] add firEmergency-plugin --- config/config.template.ini | 37 ++++++----- plugins/firEmergency/firEmergency.py | 93 ++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 plugins/firEmergency/firEmergency.py diff --git a/config/config.template.ini b/config/config.template.ini index 065650f..950f91c 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -67,8 +67,10 @@ filter_range_end = 9999999 [Plugins] #can take on or off the plugins (0|1) MySQL = 0 -BosMon = 0 httpRequest = 0 +eMail = 0 +BosMon = 0 +firEmergency = 0 # for developing template-module template = 0 @@ -86,20 +88,6 @@ tableZVEI = bos_zvei tablePOC = bos_pocsag -[BosMon] -#Server as IP of DNS-Name (without http://) -#actually no ssl supported -bosmon_server = 192.168.0.1 -bosmon_port = 80 - -#channel-name of typ "Web-Telegramm" -bosmon_channel = channel - -#Use this, when BosMon has restricted access -bosmon_user = -bosmon_password = - - [httpRequest] #URL without http:// @@ -125,6 +113,25 @@ zvei_url = poc_url = +[BosMon] +#Server as IP of DNS-Name (without http://) +#actually no ssl supported +bosmon_server = 192.168.0.1 +bosmon_port = 80 + +#channel-name of typ "Web-Telegramm" +bosmon_channel = channel + +#Use this, when BosMon has restricted access +bosmon_user = +bosmon_password = + + +[firEmergency] +firserver = localhost +firport = 9001 + + ##################### ##### Not ready yet # ##################### diff --git a/plugins/firEmergency/firEmergency.py b/plugins/firEmergency/firEmergency.py new file mode 100644 index 0000000..3b2a4b2 --- /dev/null +++ b/plugins/firEmergency/firEmergency.py @@ -0,0 +1,93 @@ +#!/usr/bin/python +# -*- coding: cp1252 -*- + +""" +firEmergency-Plugin to dispatch ZVEI- and POCSAG - messages to firEmergency + +@autor: Smith-fms + +@requires: firEmergency-Configuration has to be set in the config.ini +""" + +import logging # Global logger +import socket + +from includes import globals # Global variables + +## +# +# Main function of firEmergency-plugin +# will be called by the alarmHandler +# +def run(typ,freq,data): + """ + This function is the implementation of the firEmergency-Plugin. + It will send the data to an firEmergency-Instance. + + The configuration for the firEmergency-Connection is set in the config.ini. + + @type typ: string (ZVEI|POC) + @param typ: Typ of the dataset for sending to firEmergency + @type data: map of data (structure see interface.txt) + @param data: Contains the parameter for dispatch to firEmergency. + @type freq: string + @keyword freq: frequency is not used in this plugin + + @requires: firEmergency-Configuration has to be set in the config.ini + + @return: nothing + @exception: Exception if ConfigParser failed + @exception: Exception ifconnect to firEmergency failed + @exception: Exception if sending the data failed + """ + try: + # + #ConfigParser + # + logging.debug("reading config file") + try: + for key,val in globals.config.items("firEmergency"): + logging.debug(" - %s = %s", key, val) + except: + logging.exception("cannot read config file") + + try: + # + # connect to firEmergency + # + firSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + firSocket.connect((globals.config.get("firEmergency", "firserver"), globals.config.getint("firEmergency", "firport"))) + except: + logging.exception("cannot connect to firEmergency") + else: + # + # Format given data-structure to xml-string for firEmergency + # + if typ == "FMS": + logging.debug("FMS not supported by firEmgency") + + elif typ == "ZVEI": + logging.debug("ZVEI to firEmergency") + try: + firXML = "\n
"+data["zvei"]+"
\n"+data["zvei"]+" alarmiert.\n
\n" + firSocket.send(firXML) + except: + logging.exception("ZVEI to firEmergency failed") + + elif typ == "POC": + logging.debug("POC to firEmergency") + try: + firXML = "\n
"+data["ric"]+"
\n"+data["msg"]+"\n
\n" + firSocket.send(firXML) + except: + logging.exception("POC to firEmergency failed") + + else: + logging.warning("Invalid Typ: %s", typ) + + finally: + logging.debug("close firEmergency-Connection") + firSocket.close() + + except: + logging.exception("unknown error") \ No newline at end of file From 7857aa3146b1768dca93ef516250060352d8b2b3 Mon Sep 17 00:00:00 2001 From: JHCD Date: Thu, 4 Jun 2015 19:07:46 +0200 Subject: [PATCH 38/50] add support for poc <7 characters look likes multimon-ng doesn't print out left-hand zeros --- includes/decoders/poc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index f4fd313..4e84381 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -78,17 +78,17 @@ def decode(freq, decoded): if "POCSAG512:" in decoded: bitrate = 512 - poc_id = decoded[20:27] + poc_id = decoded[20:27].replace(" ", "").zfill(7) 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_id = decoded[21:28].replace(" ", "").zfill(7) 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_id = decoded[21:28].replace(" ", "").zfill(7) poc_sub = decoded[40].replace("3", "4").replace("2", "3").replace("1", "2").replace("0", "1") if bitrate is 0: From 5d58044c2ed889de0376a82400db1d69fc34a296 Mon Sep 17 00:00:00 2001 From: JHCD Date: Thu, 4 Jun 2015 20:13:37 +0200 Subject: [PATCH 39/50] add fms direction as Text-String to plugin interface 0 = FZG->LST 1 = LST->FZG --- includes/decoders/fms.py | 17 +++++++++-------- plugins/interface.txt | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/includes/decoders/fms.py b/includes/decoders/fms.py index 1f7dfa1..737b158 100644 --- a/includes/decoders/fms.py +++ b/includes/decoders/fms.py @@ -36,13 +36,14 @@ def decode(freq, decoded): """ timestamp = int(time.time())#Get Timestamp - 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 + 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_directionText = decoded[103:110] #Richtung (Text) + 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 @@ -52,7 +53,7 @@ def decode(freq, decoded): globals.fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new else: logging.info("FMS:%s Status:%s Richtung:%s TSI:%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} + data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "directionText":fms_directionText, "tsi":fms_tsi} from includes import alarmHandler alarmHandler.processAlarm("FMS",freq,data) diff --git a/plugins/interface.txt b/plugins/interface.txt index 3000604..e6da24b 100644 --- a/plugins/interface.txt +++ b/plugins/interface.txt @@ -14,6 +14,7 @@ FMS: - fms - status - direction +- directionText - tsi POCSAG: From 5499f6e3e0f97438c59861bf2679f912dbefe57b Mon Sep 17 00:00:00 2001 From: JHCD Date: Thu, 4 Jun 2015 20:18:44 +0200 Subject: [PATCH 40/50] add eMail-plugin for fms, zvei and pocsag plugin dispatch messages via eMail/SMTP supports: tls, user/password, wildcards in subject and message-text --- config/config.template.ini | 44 +++++++++ plugins/eMail/eMail.py | 184 +++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 plugins/eMail/eMail.py diff --git a/config/config.template.ini b/config/config.template.ini index 950f91c..4919ab3 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -113,6 +113,50 @@ zvei_url = poc_url = +[eMail] +# SMTP-Server +smtp_server = localhost +# Port of SMTP-Server (default: +smtp_port = +# use tls for connection (0|1) +tls = 0 +# Use this, when SMTP-Server has restricted access +user = +password = + +# Parameters for Alarm-Msg: +# "to" could be more than one address, comma separated +from = local@localhost +to = user@irgendwo, user2@woanders + +# Priority of the eMail: +# normal|urgent|non-urgent +priority = urgent + +# %FMS% = FMS Code +# %STATUS% = FMS Status +# %DIR% = Direction of the telegram (0/1) +# %DIRT% = Direction of the telegram (Text-String) +# %TSI% = Tactical Short Information (I-IV) +# %TIME% = Date/Time (by script) +fms_subject = FMS: %FMS% +fms_message = %TIME%: %FMS% - Status: %STATUS% - Direction: %DIRT% - TSI: %TSI% + +# %ZVEI% = ZVEI 5-tone Code +# %TIME% = Date/Time (by script) +zvei_subject = Alarm: %ZVEI% +zvei_message = %TIME%: %ZVEI% + +# %RIC% = Pocsag RIC +# %FUNC% = Pocsac function/Subric (1-4) +# %FUNCD% = Pocsac function/Subric (a-d) +# %MSG% = Message of the Pocsag telegram +# %BITRATE% = Bitrate of the Pocsag telegram +# %TIME% = Date/Time (by script) +poc_subject = Alarm: %RIC%%FUNCD% +poc_message = %TIME%: %MSG% + + [BosMon] #Server as IP of DNS-Name (without http://) #actually no ssl supported diff --git a/plugins/eMail/eMail.py b/plugins/eMail/eMail.py new file mode 100644 index 0000000..f346bd1 --- /dev/null +++ b/plugins/eMail/eMail.py @@ -0,0 +1,184 @@ +#!/usr/bin/python +# -*- coding: cp1252 -*- + +""" +eMail-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages via eMail/SMTP + +@author: Jens Herrmann + +@requires: eMail-Configuration has to be set in the config.ini +""" + +import logging # Global logger + +import time +import smtplib #for the SMTP client +from email.mime.text import MIMEText # Import the email modules we'll need +from email.utils import formatdate # need for conform to RFC2822 standard +from email.utils import make_msgid # need for conform to RFC2822 standard + +from includes import globals # Global variables + +## +# +# Private helper function for a printable Timestamp +# +def curtime(): + return time.strftime("%Y-%m-%d %H:%M:%S") + +## +# +# do send mail +# +def doSendmail(server, subject, mailtext): + """ + This local function send the eMail + + @type server: SMTP + @param server: An SMTP-Object that represents an open connection to an eMail-Server + @type subject: string + @param subject: Subject for the eMail + @type mailtext: string + @param mailtext: Mailtext for the eMail + + @return: nothing + @exception: Exception if smtp.sendmail failed + """ + try: + msg = MIMEText(mailtext) + msg['From'] = globals.config.get("eMail", "from") + msg['To'] = globals.config.get("eMail", "to") + msg['Subject'] = subject + msg['Date'] = formatdate() + msg['Message-Id'] = make_msgid() + msg['Priority'] = globals.config.get("eMail", "priority") + server.sendmail(globals.config.get("eMail", "from"), globals.config.get("eMail", "to"), msg.as_string()) + except: + logging.exception("send eMail failed") + + +## +# +# Main function of eMail-plugin +# will be called by the alarmHandler +# +def run(typ,freq,data): + """ + This function is the implementation of the eMail-Plugin. + It will send the data via eMail (SMTP) + + The configuration for the eMail-Connection is set in the config.ini. + If an user is set, the HTTP-Request is authenticatet. + + @type typ: string (FMS|ZVEI|POC) + @param typ: Typ of the dataset for sending via eMail + @type data: map of data (structure see interface.txt) + @param data: Contains the parameter for dispatch to eMail. + @type freq: string + @keyword freq: frequency of the SDR Stick + + @requires: eMail-Configuration has to be set in the config.ini + + @return: nothing + @exception: Exception if ConfigParser failed + @exception: Exception if connect to SMTP-Server failed + @exception: Exception if + """ + try: + # + # ConfigParser + # + logging.debug("reading config file") + try: + for key,val in globals.config.items("eMail"): + logging.debug(" - %s = %s", key, val) + + except: + logging.exception("cannot read config file") + + try: + # + # connect to SMTP-Server + # + server = smtplib.SMTP(globals.config.get("eMail", "smtp_server"), globals.config.get("eMail", "smtp_port")) + # debug-level to shell (0=no debug|1) + server.set_debuglevel(1) + + # if tls is enabled, starttls + if globals.config.get("eMail", "tls"): + server.starttls() + + # if user is given, login + if globals.config.get("eMail", "user"): + server.login(globals.config.get("eMail", "user"), globals.config.get("eMail", "password")) + + except: + logging.exception("cannot connect to eMail") + + else: + + if typ == "FMS": + logging.debug("Start FMS to eMail") + try: + # read subject-structure from config.ini + subject = globals.config.get("eMail", "fms_subject") + subject = subject.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards + subject = subject.replace("%DIR%", data["direction"]).replace("%DIRT%", data["directionText"]) #replace Wildcards + subject = subject.replace("%TSI%", data["tsi"]) #replace Wildcards + subject = subject.replace("%TIME%", curtime()) # replace Wildcards + # read mailtext-structure from config.ini + mailtext = globals.config.get("eMail", "fms_message") + mailtext = mailtext.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards + mailtext = mailtext.replace("%DIR%", data["direction"]).replace("%DIRT%", data["directionText"]) #replace Wildcards + mailtext = mailtext.replace("%TSI%", data["tsi"]) #replace Wildcards + mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards + # send eMail + doSendmail(server, subject, mailtext) + except: + logging.exception("FMS to eMail failed") + + elif typ == "ZVEI": + logging.debug("Start ZVEI to eMail") + try: + # read subject-structure from config.ini + subject = globals.config.get("eMail", "zvei_subject") + subject = subject.replace("%ZVEI%", data["zvei"]) #replace Wildcards + subject = subject.replace("%TIME%", curtime()) # replace Wildcards + # read mailtext-structure from config.ini + mailtext = globals.config.get("eMail", "zvei_message") + mailtext = mailtext.replace("%ZVEI%", data["zvei"]) #replace Wildcards + mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards + # send eMail + doSendmail(server, subject, mailtext) + except: + logging.exception("ZVEI to eMail failed") + + elif typ == "POC": + logging.debug("Start POC to eMail") + try: + # replace 1-4 with a-d for use as %FUNCD% + data["functionD"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") + # read subject-structure from config.ini + subject = globals.config.get("eMail", "poc_subject") + subject = subject.replace("%RIC%", data["ric"]).replace("%FUNC%", data["function"]).replace("%FUNCD%", data["functionD"]) #replace Wildcards + subject = subject.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards + subject = subject.replace("%TIME%", curtime()) # replace Wildcards + # read mailtext-structure from config.ini + mailtext = globals.config.get("eMail", "poc_message") + mailtext = mailtext.replace("%RIC%", data["ric"]).replace("%FUNC%", data["function"]).replace("%FUNCD%", data["functionD"]) #replace Wildcards + mailtext = mailtext.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards + mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards + # send eMail + doSendmail(server, subject, mailtext) + except: + logging.exception("POC to eMail failed") + + else: + logging.warning("Invalid Typ: %s", typ) + + finally: + logging.debug("close eMail-Connection") + server.quit() + + except: + logging.exception("") \ No newline at end of file From f71cbc10cad2a517187d23cd7b4cc07d22838d90 Mon Sep 17 00:00:00 2001 From: JHCD Date: Thu, 4 Jun 2015 20:21:36 +0200 Subject: [PATCH 41/50] disable debugging --- plugins/eMail/eMail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/eMail/eMail.py b/plugins/eMail/eMail.py index f346bd1..5a7b7f3 100644 --- a/plugins/eMail/eMail.py +++ b/plugins/eMail/eMail.py @@ -102,7 +102,7 @@ def run(typ,freq,data): # server = smtplib.SMTP(globals.config.get("eMail", "smtp_server"), globals.config.get("eMail", "smtp_port")) # debug-level to shell (0=no debug|1) - server.set_debuglevel(1) + server.set_debuglevel(0) # if tls is enabled, starttls if globals.config.get("eMail", "tls"): From baa9b479833a32ad4acf47978b51447b2e679959 Mon Sep 17 00:00:00 2001 From: JHCD Date: Thu, 4 Jun 2015 20:26:46 +0200 Subject: [PATCH 42/50] small documentation --- plugins/eMail/eMail.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/eMail/eMail.py b/plugins/eMail/eMail.py index 5a7b7f3..39e7645 100644 --- a/plugins/eMail/eMail.py +++ b/plugins/eMail/eMail.py @@ -14,8 +14,8 @@ import logging # Global logger import time import smtplib #for the SMTP client from email.mime.text import MIMEText # Import the email modules we'll need -from email.utils import formatdate # need for conform to RFC2822 standard -from email.utils import make_msgid # need for conform to RFC2822 standard +from email.utils import formatdate # need for confirm to RFC2822 standard +from email.utils import make_msgid # need for confirm to RFC2822 standard from includes import globals # Global variables @@ -24,6 +24,10 @@ from includes import globals # Global variables # Private helper function for a printable Timestamp # def curtime(): + """ + Local function + @return: current time as formatted string + """ return time.strftime("%Y-%m-%d %H:%M:%S") ## @@ -82,7 +86,7 @@ def run(typ,freq,data): @return: nothing @exception: Exception if ConfigParser failed @exception: Exception if connect to SMTP-Server failed - @exception: Exception if + @exception: Exception if sending the eMail failed """ try: # From 86951d23ad5510234c27bba9b9f438dc64944d6d Mon Sep 17 00:00:00 2001 From: Bastian Schroll Date: Fri, 5 Jun 2015 08:45:11 +0200 Subject: [PATCH 43/50] edit readme.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c721dfe..b6baf69 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,13 @@ unless you are developer you can use the develop-Branch - may be unstable! - MySQL (insert Data into MySQL Database [FMS|ZVEI|POC]) - BosMon (send Data to BosMon Server [FMS|ZVEI|POC]) - httpRequest (send a request with parameter to an URL [FMS|ZVEI|POC]) -for more Information to the Plugins see `config.ini` +- eMail (send Mails [FMS|ZVEI|POC]) +- firEmergency [ZVEI|POC] + +- for more Information to the Plugins see `config.ini` ##### Plugins for the Future: -- E-mail Notification -- Other Ideas per Issues +- Ideas per Issues please ### Configuration From 9de516ef45f40299b1ff51019e2ec23a3f9ed3c9 Mon Sep 17 00:00:00 2001 From: JHCD Date: Fri, 5 Jun 2015 09:10:11 +0200 Subject: [PATCH 44/50] some small changes... --- README.md | 84 +++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index b6baf69..eb2c636 100644 --- a/README.md +++ b/README.md @@ -12,30 +12,30 @@ The software was developed using the Multimon-NG code, a function in the real op unless you are developer you can use the develop-Branch - may be unstable! ### Features -##### Implemented Features: -- FMS, ZVEI and POCSAG512/1200/2400 decoding and Displaying -- Plugin support for easy Functions extension +##### Implemented features: +- FMS, ZVEI and POCSAG512/1200/2400 decoding and displaying +- Plugin support for easy functional extension - Filtering double alarms with adjustable time -- Filtering Allowed, Denied and Range of POCSAG RIC´s -- Filtering Data for each Typ/Plugin combination with RegEX -- All configurations in seperate config File +- Filtering allowed, denied and range of POCSAG RIC´s +- Filtering data for each typ/plugin combination with RegEX +- All configurations in a seperate config file - Data validation (plausibility test) -- Logfiles for better Troubleshooting -- verbose/quiet Mode for more/none information +- Logfiles for better troubleshooting +- verbose/quiet mode for more/none information -##### Features for the Future: -- more Plugins +##### Features for the future: +- more plugins ###Plugins -##### Implemented Plugins: -- MySQL (insert Data into MySQL Database [FMS|ZVEI|POC]) -- BosMon (send Data to BosMon Server [FMS|ZVEI|POC]) +##### Implemented plugins: +- MySQL (insert data into MySQL database [FMS|ZVEI|POC]) - httpRequest (send a request with parameter to an URL [FMS|ZVEI|POC]) - eMail (send Mails [FMS|ZVEI|POC]) -- firEmergency [ZVEI|POC] +- BosMon (send data to BosMon server [FMS|ZVEI|POC]) +- firEmergency (send data to firEmergency server [ZVEI|POC]) -- for more Information to the Plugins see `config.ini` +- for more Information to the plugins see `config.ini` ##### Plugins for the Future: - Ideas per Issues please @@ -43,44 +43,44 @@ unless you are developer you can use the develop-Branch - may be unstable! ### Configuration ##### boswatch.py -Take a look into the Folder /config/ +Take a look into the folder /config/ Rename `config.template.ini` to `config.ini` In the Section `[BOSWatch]` you can set double_alarm_time etc. In the Section `[Plugins]` you can activate or deactivate the Plugins -For each Plugin that requires configurations a own Section with his Name is available +For each plugin that requires configurations, a own Section with his name is available -For the other Functions see "Usage" below. +For the other functions see "Usage" below. ##### Filtering Functions (RegEX) -For the RegEX Filter Functions see Section `[Filters]` -http://www.regexr.com/ - RegEX Test Tool an Documentation -No Filter for a Typ/Plugin Combination = all Data pass +For the RegEX filter functions see Section `[Filters]` +http://www.regexr.com/ - RegEX test tool an documentation +No filter for a combination typ/plugin = all data will pass Syntax: INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX (separator ";") -- TYP = the Data Typ (FMS|ZVEI|POC) -- DATAFIELD = the field of the Data Array (See interface.txt) -- PLUGIN = the name of the Plugin to call with this Filter (* for all) -- FREQUENZ = the Frequenz to use the Filter (for more SDR Sticks (* for all)) +- TYP = the data typ (FMS|ZVEI|POC) +- DATAFIELD = the field of the data array (See interface.txt) +- PLUGIN = the name of the plugin to call with this filter (* for all) +- FREQUENZ = the frequenz to use the filter (for more SDR sticks (* for all)) - REGEX = the RegEX -only ZVEI to all Plugins with 25### at 85.5MHz +only ZVEI to all plugins with 25### at 85.5MHz testfilter = ZVEI;zvei;*;85500000;25[0-9]{3} -only POCSAG to MySQL with the text "ALARM:" in the Message +only POCSAG to MySQL with the text "ALARM:" in the message pocTest = POC;msg;MySQL;*;ALARM: -##### Web Frontend -Put the Files in Folder /wwww/ into your local Webserver Folder (/var/www/). -Now you must edit the "config.php" with your Userdata to your local Database. -For the Parsing Functions take a look into the parser.php +##### Web frontend +Put the files in folder /wwww/ into your local webserver folder (f.e. /var/www/). +Now you must edit the "config.php" with your userdata to your local database. +Take a look into the parser.php for the parsing functions ### Usage `sudo python boswatch.py -f 85.235M -a FMS ZVEI` -Starts boswatch at Frequency 85.235 MHz with the Demodulation Functions FMS and ZVEI. +Starts boswatch at frequency 85.235 MHz with the demodulation functions FMS and ZVEI. Parameter -f/--freq and -a/--demod are required! -Help to all usable Parameters with `sudo python boswatch.py -h` +Help to all usable parameters with `sudo python boswatch.py -h` ``` usage: boswatch.py [-h] -f FREQ [-d DEVICE] [-e ERROR] -a @@ -91,28 +91,28 @@ optional arguments: -h, --help show this help message and exit -f FREQ, --freq FREQ Frequency you want to listen -d DEVICE, --device DEVICE Device you want to use (Check with rtl_test) - -e ERROR, --error ERROR Frequency-Error of your Device in PPM + -e ERROR, --error ERROR Frequency-Error of your device in PPM -a {FMS,ZVEI,POC512,POC1200,POC2400} [{FMS,ZVEI,POC512,POC1200,POC2400} ...], --demod {FMS,ZVEI,POC512,POC1200,POC2400} [{FMS,ZVEI,POC512,POC1200,POC2400} ...] - Demodulation Functions - -s SQUELCH, --squelch SQUELCH Level of Squelch - -v, --verbose Shows more Information - -q, --quiet Shows no Information. Only Logfiles + Demodulation functions + -s SQUELCH, --squelch SQUELCH level of squelch + -v, --verbose Shows more information + -q, --quiet Shows no information. Only logfiles ``` ### Installation You can easy install BOSWatch with the install.sh Script. -- Download the install.sh in any Folder you want. +- Download the install.sh in any folder you want. - Make it executeable `sudo chmod +x install.sh` - And use the script `sudo sh install.sh` Now the script downloads and compile all needed data. At the end you can find BOSWatch in `~/boswatch/` Rename `config.template.ini` to `config.ini` and configure -In case of an Error during the Installation, check the Logfile in `~/boswatch/install/setup_log.txt` +In case of an error during the installation, check the logfile in `~/boswatch/install/setup_log.txt` -Caution, script don't install a Webserver with PHP and MySQL. +Caution, script don't install a webserver with PHP and MySQL. So you have to make up manually if you want to use MySQL support. @@ -120,4 +120,4 @@ So you have to make up manually if you want to use MySQL support. - RTL_SDR (rtl_fm) - Multimon-NG - Python Support -- MySQL Connector for Python +- MySQL Connector for Python (for MySQL-plugin) From 7c5e22357969be0f2b4adc8edfac2401cf3c0cce Mon Sep 17 00:00:00 2001 From: JHCD Date: Fri, 5 Jun 2015 09:43:15 +0200 Subject: [PATCH 45/50] undo docu in case of parsing-error :-/ --- plugins/eMail/eMail.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/eMail/eMail.py b/plugins/eMail/eMail.py index 39e7645..4c22f0f 100644 --- a/plugins/eMail/eMail.py +++ b/plugins/eMail/eMail.py @@ -26,9 +26,10 @@ from includes import globals # Global variables def curtime(): """ Local function - @return: current time as formatted string + @return timeString: current time as formatted string """ - return time.strftime("%Y-%m-%d %H:%M:%S") + timeString = time.strftime("%Y-%m-%d %H:%M:%S") + return timeString ## # From fedf879a06028afe414fc4c987322c230bf5a065 Mon Sep 17 00:00:00 2001 From: JHCD Date: Fri, 5 Jun 2015 09:47:17 +0200 Subject: [PATCH 46/50] undo docu in case of parsing-error :-/ --- plugins/eMail/eMail.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/eMail/eMail.py b/plugins/eMail/eMail.py index 4c22f0f..b7b1d71 100644 --- a/plugins/eMail/eMail.py +++ b/plugins/eMail/eMail.py @@ -24,12 +24,7 @@ from includes import globals # Global variables # Private helper function for a printable Timestamp # def curtime(): - """ - Local function - @return timeString: current time as formatted string - """ - timeString = time.strftime("%Y-%m-%d %H:%M:%S") - return timeString + return time.strftime("%Y-%m-%d %H:%M:%S") ## # From 50ce6b2671945bfe477131afb0b297df5112b781 Mon Sep 17 00:00:00 2001 From: JHCD Date: Fri, 5 Jun 2015 10:06:33 +0200 Subject: [PATCH 47/50] fix bug in handling when arg -h/--help is used Problem: Some unexpected exception are thrown, when arg war -h Reason: ArgParser throws SystemExit exception when -h is used Solution: 1.) Move ArgParser to the first position, to prevent create the objects 2.) make sure, the finally block of the main program will not be called 3.) catch SystemExit exception --- boswatch.py | 280 +++++++++++++++++++++++++++------------------------- 1 file changed, 143 insertions(+), 137 deletions(-) diff --git a/boswatch.py b/boswatch.py index da818fa..257fb25 100755 --- a/boswatch.py +++ b/boswatch.py @@ -22,6 +22,8 @@ import os #for log mkdir import time #timestamp import subprocess +import sys + from includes import globals # Global variables ## @@ -58,12 +60,37 @@ def freqToHz(freq): except: logging.exception("Error in freqToHz()") - + +# +# Manage args parsing +# +try: + # With -h or --help you get the Args help + # ArgsParser + parser = argparse.ArgumentParser(prog="boswatch.py", + description="BOSWatch is a Python Script to recive and decode german BOS information with rtl_fm and multimon-NG", + epilog="More options you can find in the extern config.ini file in the folder /config") + # parser.add_argument("-c", "--channel", help="BOS Channel you want to listen") + parser.add_argument("-f", "--freq", help="Frequency you want to listen", required=True) + parser.add_argument("-d", "--device", help="Device you want to use (Check with rtl_test)", type=int, default=0) + parser.add_argument("-e", "--error", help="Frequency-Error of your device in PPM", type=int, default=0) + parser.add_argument("-a", "--demod", help="Demodulation functions", choices=['FMS', 'ZVEI', 'POC512', 'POC1200', 'POC2400'], required=True, nargs="+") + parser.add_argument("-s", "--squelch", help="Level of squelch", type=int, default=0) + parser.add_argument("-v", "--verbose", help="Shows more information", action="store_true") + parser.add_argument("-q", "--quiet", help="Shows no information. Only logfiles", action="store_true") + args = parser.parse_args() +except SystemExit: + # -h or --help called, exit now + exit(0) +except: + print "Unexpected error parsing the arguments" + + # # Programm # try: - try: + try: # # Script-pathes # @@ -74,7 +101,7 @@ try: # if not os.path.exists(globals.script_path+"/log/"): os.mkdir(globals.script_path+"/log/") - + # # Create new myLogger... # @@ -116,157 +143,136 @@ try: except: logging.exception("cannot clear Logfiles") + try: + # + # For debug display/log args + # + logging.debug(" - Frequency: %s", freqToHz(args.freq)) + logging.debug(" - Device: %s", args.device) + logging.debug(" - PPM Error: %s", args.error) + logging.debug(" - Squelch: %s", args.squelch) + + demodulation = "" + if "FMS" in args.demod: + demodulation += "-a FMSFSK " + logging.debug(" - Demod: FMS") + if "ZVEI" in args.demod: + demodulation += "-a ZVEI2 " + logging.debug(" - Demod: ZVEI") + if "POC512" in args.demod: + demodulation += "-a POCSAG512 " + logging.debug(" - Demod: POC512") + if "POC1200" in args.demod: + demodulation += "-a POCSAG1200 " + logging.debug(" - Demod: POC1200") + if "POC2400" in args.demod: + demodulation += "-a POCSAG2400 " + logging.debug(" - Demod: POC2400") + + logging.debug(" - Verbose Mode: %s", args.verbose) + logging.debug(" - Quiet Mode: %s", args.quiet) + + if args.verbose: + ch.setLevel(logging.DEBUG) + if args.quiet: + ch.setLevel(logging.CRITICAL) + + if not args.quiet: #only if not quiet mode + from includes import shellHeader + shellHeader.printHeader(args) + except: + logging.exception("cannot display/log args") + try: # - # Parse args + # Read config.ini # - logging.debug("parse args") - #With -h or --help you get the Args help - #ArgsParser - parser = argparse.ArgumentParser(prog="boswatch.py", description="BOSWatch is a Python Script to Recive and Decode German BOS Information with rtl_fm and multimon-NG", epilog="More Options you can find in the extern config.ini File in this Folder") - #parser.add_argument("-c", "--channel", help="BOS Channel you want to listen") - parser.add_argument("-f", "--freq", help="Frequency you want to listen", required=True) - parser.add_argument("-d", "--device", help="Device you want to use (Check with rtl_test)", type=int, default=0) - parser.add_argument("-e", "--error", help="Frequency-Error of your Device in PPM", type=int, default=0) - parser.add_argument("-a", "--demod", help="Demodulation Functions", choices=['FMS', 'ZVEI', 'POC512', 'POC1200', 'POC2400'], required=True, nargs="+") - parser.add_argument("-s", "--squelch", help="Level of Squelch", type=int, default=0) - parser.add_argument("-v", "--verbose", help="Shows more Information", action="store_true") - parser.add_argument("-q", "--quiet", help="Shows no Information. Only Logfiles", action="store_true") - args = parser.parse_args() + logging.debug("reading config file") + globals.config = ConfigParser.ConfigParser() + globals.config.read(globals.script_path+"/config/config.ini") + for key,val in globals.config.items("BOSWatch"): + logging.debug(" - %s = %s", key, val) except: - logging.error("cannot parse args") - else: + logging.exception("cannot read config file") + else: - try: - # - # For debug display/log args - # - logging.debug(" - Frequency: %s", freqToHz(args.freq)) - logging.debug(" - Device: %s", args.device) - logging.debug(" - PPM Error: %s", args.error) - logging.debug(" - Squelch: %s", args.squelch) - - demodulation = "" - if "FMS" in args.demod: - demodulation += "-a FMSFSK " - logging.debug(" - Demod: FMS") - if "ZVEI" in args.demod: - demodulation += "-a ZVEI2 " - logging.debug(" - Demod: ZVEI") - if "POC512" in args.demod: - demodulation += "-a POCSAG512 " - logging.debug(" - Demod: POC512") - if "POC1200" in args.demod: - demodulation += "-a POCSAG1200 " - logging.debug(" - Demod: POC1200") - if "POC2400" in args.demod: - demodulation += "-a POCSAG2400 " - logging.debug(" - Demod: POC2400") - - logging.debug(" - Verbose Mode: %s", args.verbose) - logging.debug(" - Quiet Mode: %s", args.quiet) - - if args.verbose: - ch.setLevel(logging.DEBUG) - if args.quiet: - ch.setLevel(logging.CRITICAL) - - if not args.quiet: #only if not quiet mode - from includes import shellHeader - shellHeader.printHeader(args) - except: - logging.exception("cannot display/log args") - try: + # + # Set the loglevel and backupCount of the file handler # - # Read config.ini - # - logging.debug("reading config file") - globals.config = ConfigParser.ConfigParser() - globals.config.read(globals.script_path+"/config/config.ini") - for key,val in globals.config.items("BOSWatch"): - logging.debug(" - %s = %s", key, val) + logging.debug("set loglevel of fileHandler to: %s",globals.config.getint("BOSWatch","loglevel") ) + fh.setLevel(globals.config.getint("BOSWatch","loglevel")) + logging.debug("set backupCount of fileHandler to: %s", globals.config.getint("BOSWatch","backupCount")) + fh.setBackupCount(globals.config.getint("BOSWatch","backupCount")) except: - logging.exception("cannot read config file") - else: + logging.exception("cannot set loglevel of fileHandler") + + # + # Load plugins + # + from includes import pluginLoader + pluginLoader.loadPlugins() + + # + # Load filters + # + if globals.config.getint("BOSWatch","useRegExFilter"): + from includes import filter + filter.loadFilters() + + try: + # + # Start rtl_fm + # + logging.debug("starting rtl_fm") + rtl_fm = subprocess.Popen("rtl_fm -d "+str(args.device)+" -f "+str(freqToHz(args.freq))+" -M fm -s 22050 -p "+str(args.error)+" -E DC -F 0 -l "+str(args.squelch)+" -g 100", + #stdin=rtl_fm.stdout, + stdout=subprocess.PIPE, + stderr=open(globals.script_path+"/log/rtl_fm.log","a"), + shell=True) + except: + logging.exception("cannot start rtl_fm") + else: try: - # - # Set the loglevel and backupCount of the file handler # - logging.debug("set loglevel of fileHandler to: %s",globals.config.getint("BOSWatch","loglevel") ) - fh.setLevel(globals.config.getint("BOSWatch","loglevel")) - logging.debug("set backupCount of fileHandler to: %s", globals.config.getint("BOSWatch","backupCount")) - fh.setBackupCount(globals.config.getint("BOSWatch","backupCount")) + # Start multimon + # + logging.debug("starting multimon-ng") + multimon_ng = subprocess.Popen("multimon-ng "+str(demodulation)+" -f alpha -t raw /dev/stdin - ", + stdin=rtl_fm.stdout, + stdout=subprocess.PIPE, + stderr=open(globals.script_path+"/log/multimon.log","a"), + shell=True) except: - logging.exception("cannot set loglevel of fileHandler") - - # - # Load plugins - # - from includes import pluginLoader - pluginLoader.loadPlugins() - - # - # Load filters - # - if globals.config.getint("BOSWatch","useRegExFilter"): - from includes import filter - filter.loadFilters() - - try: - # - # Start rtl_fm - # - logging.debug("starting rtl_fm") - rtl_fm = subprocess.Popen("rtl_fm -d "+str(args.device)+" -f "+str(freqToHz(args.freq))+" -M fm -s 22050 -p "+str(args.error)+" -E DC -F 0 -l "+str(args.squelch)+" -g 100", - #stdin=rtl_fm.stdout, - stdout=subprocess.PIPE, - stderr=open(globals.script_path+"/log/rtl_fm.log","a"), - shell=True) - except: - logging.exception("cannot start rtl_fm") - else: + logging.exception("cannot start multimon-ng") + else: - try: + logging.debug("start decoding") + + while True: # - # Start multimon + # Get decoded data from multimon-ng and call BOSWatch-decoder # - logging.debug("starting multimon-ng") - multimon_ng = subprocess.Popen("multimon-ng "+str(demodulation)+" -f alpha -t raw /dev/stdin - ", - stdin=rtl_fm.stdout, - stdout=subprocess.PIPE, - stderr=open(globals.script_path+"/log/multimon.log","a"), - shell=True) - except: - logging.exception("cannot start multimon-ng") - else: + + # RAW Data from Multimon-NG + # ZVEI2: 25832 + # FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n' + # POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World + #decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout - logging.debug("start decoding") + # Test-strings 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=I (ohneNA,ohneSIGNAL)) CRC correct\n'" + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'" + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=II (ohneNA,mit SIGNAL)) CRC correct\n'" + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct\n'" + #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=IV (mit NA,mit SIGNAL)) CRC correct\n'" + #decoded = "POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World" + #time.sleep(1) - while True: - # - # Get decoded data from multimon-ng and call BOSWatch-decoder - # - - # RAW Data from Multimon-NG - # ZVEI2: 25832 - # FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n' - # POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World - decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout - - # Test-strings 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=I (ohneNA,ohneSIGNAL)) CRC correct\n'" - #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'" - #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=II (ohneNA,mit SIGNAL)) CRC correct\n'" - #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct\n'" - #decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=IV (mit NA,mit SIGNAL)) CRC correct\n'" - #decoded = "POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World" - #time.sleep(1) - - from includes import decoder - decoder.decode(freqToHz(args.freq), decoded) + from includes import decoder + decoder.decode(freqToHz(args.freq), decoded) except KeyboardInterrupt: logging.warning("Keyboard Interrupt") From 2d8c5e4bf5fa368fa9063d5168c7400e22faa9d4 Mon Sep 17 00:00:00 2001 From: JHCD Date: Fri, 5 Jun 2015 10:19:49 +0200 Subject: [PATCH 48/50] some small documentation --- boswatch.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/boswatch.py b/boswatch.py index 257fb25..0203e8e 100755 --- a/boswatch.py +++ b/boswatch.py @@ -22,8 +22,6 @@ import os #for log mkdir import time #timestamp import subprocess -import sys - from includes import globals # Global variables ## @@ -62,11 +60,10 @@ def freqToHz(freq): # -# Manage args parsing +# ArgParser # try: # With -h or --help you get the Args help - # ArgsParser parser = argparse.ArgumentParser(prog="boswatch.py", description="BOSWatch is a Python Script to recive and decode german BOS information with rtl_fm and multimon-NG", epilog="More options you can find in the extern config.ini file in the folder /config") @@ -80,16 +77,17 @@ try: parser.add_argument("-q", "--quiet", help="Shows no information. Only logfiles", action="store_true") args = parser.parse_args() except SystemExit: - # -h or --help called, exit now + # -h or --help called, exit right now exit(0) except: - print "Unexpected error parsing the arguments" + print "cannot parsing the arguments" # -# Programm +# Main Programm # try: + # initialization try: # # Script-pathes @@ -126,6 +124,7 @@ try: except: logging.exception("cannot create logger") else: + # initialization of the logging was fine, continue... try: # @@ -195,6 +194,7 @@ try: except: logging.exception("cannot read config file") else: + # initialization of was fine, continue with main program... try: # @@ -233,6 +233,7 @@ try: except: logging.exception("cannot start rtl_fm") else: + # rtl_fm started, continue... try: # @@ -247,6 +248,7 @@ try: except: logging.exception("cannot start multimon-ng") else: + # multimon-ng started, continue... logging.debug("start decoding") @@ -295,4 +297,4 @@ finally: logging.shutdown() fh.close() ch.close() - exit(0) + exit(0) \ No newline at end of file From 300675a0e3b9d1db24b01355df5e1bc12e2fbae3 Mon Sep 17 00:00:00 2001 From: JHCD Date: Fri, 5 Jun 2015 11:26:01 +0200 Subject: [PATCH 49/50] move creation of ric-sub as char to poc-decoder --- boswatch.py | 4 ++-- config/config.template.ini | 4 ++-- includes/decoders/poc.py | 2 ++ plugins/BosMon/BosMon.py | 3 +-- plugins/eMail/eMail.py | 8 ++++---- plugins/interface.txt | 1 + 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/boswatch.py b/boswatch.py index 0203e8e..320a19d 100755 --- a/boswatch.py +++ b/boswatch.py @@ -194,7 +194,7 @@ try: except: logging.exception("cannot read config file") else: - # initialization of was fine, continue with main program... + # initialization was fine, continue with main program... try: # @@ -261,7 +261,7 @@ try: # ZVEI2: 25832 # FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n' # POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World - #decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout + decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout # Test-strings only for develop #decoded = "ZVEI2: 25832" diff --git a/config/config.template.ini b/config/config.template.ini index 4919ab3..0a7ebec 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -149,11 +149,11 @@ zvei_message = %TIME%: %ZVEI% # %RIC% = Pocsag RIC # %FUNC% = Pocsac function/Subric (1-4) -# %FUNCD% = Pocsac function/Subric (a-d) +# %FUNCCHAR% = Pocsac function/Subric als character (a-d) # %MSG% = Message of the Pocsag telegram # %BITRATE% = Bitrate of the Pocsag telegram # %TIME% = Date/Time (by script) -poc_subject = Alarm: %RIC%%FUNCD% +poc_subject = Alarm: %RIC%%FUNCCHAR% poc_message = %TIME%: %MSG% diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index 4e84381..3aab240 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -111,6 +111,8 @@ def decode(freq, decoded): else: 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} + # Add function as character a-d to dataset + data["functionChar"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") from includes import alarmHandler alarmHandler.processAlarm("POC",freq,data) diff --git a/plugins/BosMon/BosMon.py b/plugins/BosMon/BosMon.py index e91351a..968c423 100644 --- a/plugins/BosMon/BosMon.py +++ b/plugins/BosMon/BosMon.py @@ -157,8 +157,7 @@ def run(typ,freq,data): logging.debug("Start POC to BosMon") try: # BosMon-Telegramin expected "a-d" as RIC-sub/function - data["function"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") - params = urllib.urlencode({'type':'pocsag', 'address':data["ric"], 'flags':'0', 'function':data["function"], 'message':data["msg"]}) + params = urllib.urlencode({'type':'pocsag', 'address':data["ric"], 'flags':'0', 'function':data["functionChar"], 'message':data["msg"]}) logging.debug(" - Params: %s", params) # dispatch the BosMon-request bosMonRequest(httprequest, params, headers) diff --git a/plugins/eMail/eMail.py b/plugins/eMail/eMail.py index b7b1d71..842d013 100644 --- a/plugins/eMail/eMail.py +++ b/plugins/eMail/eMail.py @@ -156,16 +156,16 @@ def run(typ,freq,data): elif typ == "POC": logging.debug("Start POC to eMail") try: - # replace 1-4 with a-d for use as %FUNCD% - data["functionD"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") # read subject-structure from config.ini subject = globals.config.get("eMail", "poc_subject") - subject = subject.replace("%RIC%", data["ric"]).replace("%FUNC%", data["function"]).replace("%FUNCD%", data["functionD"]) #replace Wildcards + subject = subject.replace("%RIC%", data["ric"]) #replace Wildcards + subject = subject.replace("%FUNC%", data["function"]).replace("%FUNCCHAR%", data["functionChar"]) #replace Wildcards subject = subject.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards subject = subject.replace("%TIME%", curtime()) # replace Wildcards # read mailtext-structure from config.ini mailtext = globals.config.get("eMail", "poc_message") - mailtext = mailtext.replace("%RIC%", data["ric"]).replace("%FUNC%", data["function"]).replace("%FUNCD%", data["functionD"]) #replace Wildcards + mailtext = mailtext.replace("%RIC%", data["ric"]) #replace Wildcards + mailtext = mailtext.replace("%FUNC%", data["function"]).replace("%FUNCCHAR%", data["functionChar"]) #replace Wildcards mailtext = mailtext.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards # send eMail diff --git a/plugins/interface.txt b/plugins/interface.txt index e6da24b..821729d 100644 --- a/plugins/interface.txt +++ b/plugins/interface.txt @@ -20,6 +20,7 @@ FMS: POCSAG: - ric - function +- functionChar - msg - bitrate From f1f6503198e27c8fc35ceeccf3c937675ff42298 Mon Sep 17 00:00:00 2001 From: JHCD Date: Fri, 5 Jun 2015 21:49:51 +0200 Subject: [PATCH 50/50] new functionality: lookUp description in a given csv-file for FMS, ZVEI and POC you have the possibility to set a discription for every FMS, ZWEI oder RIC address descriptions will be imported via csv-files if enabled BOSWatch will look up the description and could be used in the plugins eMail-plugin extended wildcards to use describtion %DESC% --- boswatch.py | 9 +++- config/config.template.ini | 101 ++++++++++++++++++++--------------- csv/fms.csv | 10 ++++ csv/poc.csv | 10 ++++ csv/zvei.csv | 10 ++++ includes/decoders/fms.py | 7 ++- includes/decoders/poc.py | 7 ++- includes/decoders/zvei.py | 7 ++- includes/descriptionList.py | 102 ++++++++++++++++++++++++++++++++++++ includes/globals.py | 8 ++- plugins/eMail/eMail.py | 6 +++ plugins/interface.txt | 3 ++ 12 files changed, 234 insertions(+), 46 deletions(-) create mode 100644 csv/fms.csv create mode 100644 csv/poc.csv create mode 100644 csv/zvei.csv create mode 100644 includes/descriptionList.py diff --git a/boswatch.py b/boswatch.py index 320a19d..c6e8b2e 100755 --- a/boswatch.py +++ b/boswatch.py @@ -106,7 +106,7 @@ try: myLogger = logging.getLogger() myLogger.setLevel(logging.DEBUG) #set log string format - formatter = logging.Formatter('%(asctime)s - %(module)-12s [%(levelname)-8s] %(message)s', '%d.%m.%Y %H:%M:%S') + formatter = logging.Formatter('%(asctime)s - %(module)-15s [%(levelname)-8s] %(message)s', '%d.%m.%Y %H:%M:%S') #create a file logger fh = MyTimedRotatingFileHandler(globals.script_path+"/log/boswatch.log", "midnight", interval=1, backupCount=999) #Starts with log level >= Debug @@ -220,6 +220,13 @@ try: from includes import filter filter.loadFilters() + # + # Load description lists + # + if globals.config.getint("BOSWatch","useDescription"): + from includes import descriptionList + descriptionList.loadDescriptionLists() + try: # # Start rtl_fm diff --git a/config/config.template.ini b/config/config.template.ini index 0a7ebec..64d2137 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -3,69 +3,86 @@ ######################## [BOSWatch] -#set loglevel for logfile -#10 = debug -#20 = info -#30 = warning -#40 = error -#50 = critical +# set loglevel for logfile +# 10 = debug +# 20 = info +# 30 = warning +# 40 = error +# 50 = critical loglevel = 10 -#BOSWatch use a rotating logfile -#Rotating is at midnight -#You can set the backupCount here -#backupCount = 7 (keeps logfiles for a week) +# BOSWatch use a rotating logfile +# Rotating is at midnight +# You can set the backupCount here +# backupCount = 7 (keeps logfiles for a week) backupCount = 7 -#Using RegEx-Filter (0|1) -#Filter-configuration in section [Filters] +# Using RegEx-Filter (0|1) +# Filter-configuration in section [Filters] useRegExFilter = 0 +# Using Description (0|1) +# You have to be enabled it for every typ in the sections below too +useDescription = 0 + [FMS] -#time to ignore same alarm in a row (sek) +# time to ignore same alarm in a row (sek) double_ignore_time = 5 +# look-up-table for adding a description +# turn on functionality (0|1) +idDescribed = 0 + [ZVEI] -#time to ignore same alarm in a row (sek) +# time to ignore same alarm in a row (sek) double_ignore_time = 5 +# look-up-table for adding a description +# turn on functionality (0|1) +idDescribed = 0 + [POC] -#time to ignore same alarm in a row (sek) +# time to ignore same alarm in a row (sek) double_ignore_time = 5 -#some very simple filters: -#Allow only this RICs (empty: allow all, separator ",") -#f.e.: allow_ric = 1234566,1234567,1234568 +# some very simple filters: +# Allow only this RICs (empty: allow all, separator ",") +# f.e.: allow_ric = 1234566,1234567,1234568 allow_ric = -#Deny this RICs (empty: allow all, separator ",") -#f.e.: deny_ric = 1234566,1234567,1234568 + +# Deny this RICs (empty: allow all, separator ",") +# f.e.: deny_ric = 1234566,1234567,1234568 deny_ric = -#start and end of an allowed filter range +# start and end of an allowed filter range filter_range_start = 0000000 filter_range_end = 9999999 +# look-up-table for adding a description +# turn on functionality (0|1) +idDescribed = 0 + [Filters] -#RegEX Filter Configuration -#http://www.regexr.com/ - RegEX Test Tool an Documentation -#No Filter for a Typ/Plugin Combination = all Data pass -#INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX -#TYP = the Data Typ (FMS|ZVEI|POC) -#DATAFIELD = the field of the Data Array (See interface.txt) -#PLUGIN = the name of the Plugin to call with this Filter (* for all) -#FREQUENZ = the Frequenz to use the Filter (for more SDR Sticks (* for all)) -#REGEX = the RegEX +# RegEX Filter Configuration +# http://www.regexr.com/ - RegEX Test Tool an Documentation +# No Filter for a Typ/Plugin Combination = all Data pass +# INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX +# TYP = the Data Typ (FMS|ZVEI|POC) +# DATAFIELD = the field of the Data Array (See interface.txt) +# PLUGIN = the name of the Plugin to call with this Filter (* for all) +# FREQUENZ = the Frequenz to use the Filter (for more SDR Sticks (* for all)) +# REGEX = the RegEX -#only ZVEI to all Plugins with 25### at 85.5MHz +# only ZVEI to all Plugins with 25### at 85.5MHz #testfilter = ZVEI;zvei;*;85500000;25[0-9]{3} -#only POCSAG to MySQL with the text "ALARM:" in the Message +# only POCSAG to MySQL with the text "ALARM:" in the Message #pocTest = POC;msg;MySQL;*;ALARM: [Plugins] -#can take on or off the plugins (0|1) +# can take on or off the plugins (0|1) MySQL = 0 httpRequest = 0 eMail = 0 @@ -77,22 +94,23 @@ template = 0 [MySQL] +# MySQL configuration dbserver = localhost dbuser = root dbpassword = root database = boswatch -#tables in the database +# tables in the database tableFMS = bos_fms tableZVEI = bos_zvei tablePOC = bos_pocsag [httpRequest] -#URL without http:// +# URL without http:// -#you can use the following wildcards in your URL as GET params: -#http://en.wikipedia.org/wiki/Query_string +# you can use the following wildcards in your URL as GET params: +# http://en.wikipedia.org/wiki/Query_string # %FMS% = FMS Code # %STATUS% = FMS Status @@ -158,20 +176,21 @@ poc_message = %TIME%: %MSG% [BosMon] -#Server as IP of DNS-Name (without http://) -#actually no ssl supported +# Server as IP of DNS-Name (without http://) +# actually no ssl supported bosmon_server = 192.168.0.1 bosmon_port = 80 -#channel-name of typ "Web-Telegramm" +# channel-name of typ "Web-Telegramm" bosmon_channel = channel -#Use this, when BosMon has restricted access +# Use this, when BosMon has restricted access bosmon_user = bosmon_password = [firEmergency] +# firEmergency configuration firserver = localhost firport = 9001 diff --git a/csv/fms.csv b/csv/fms.csv new file mode 100644 index 0000000..e52a17e --- /dev/null +++ b/csv/fms.csv @@ -0,0 +1,10 @@ +fms,description +# +# BOSWatch CSV file for describing FMS-Addresses +# +# For each FMS-Address you could set a description-text +# Use the structure: fms,"Description-Text" +# +# !!! DO NOT delete the first line !!! +# +93377141,"John Q. Publics car" \ No newline at end of file diff --git a/csv/poc.csv b/csv/poc.csv new file mode 100644 index 0000000..3a667fe --- /dev/null +++ b/csv/poc.csv @@ -0,0 +1,10 @@ +ric,description +# +# BOSWatch CSV file for describing POCSAG-Addresses +# +# For each RIC-Address you could set a description-text +# Use the structure: ric,"Description-Text" +# +# !!! DO NOT delete the first line !!! +# +1234567,"John Q. Public" \ No newline at end of file diff --git a/csv/zvei.csv b/csv/zvei.csv new file mode 100644 index 0000000..da78fe3 --- /dev/null +++ b/csv/zvei.csv @@ -0,0 +1,10 @@ +zvei,description +# +# BOSWatch CSV file for describing ZVEI-Addresses +# +# For each ZVEI-Address you could set a description-text +# Use the structure: zvei,"Description-Text" +# +# !!! DO NOT delete the first line !!! +# +25832,"John Q. Public" \ No newline at end of file diff --git a/includes/decoders/fms.py b/includes/decoders/fms.py index 737b158..cc542b9 100644 --- a/includes/decoders/fms.py +++ b/includes/decoders/fms.py @@ -53,7 +53,12 @@ def decode(freq, decoded): globals.fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new else: logging.info("FMS:%s Status:%s Richtung:%s TSI:%s", fms_id[0:8], fms_status, fms_direction, fms_tsi) - data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "directionText":fms_directionText, "tsi":fms_tsi} + data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "directionText":fms_directionText, "tsi":fms_tsi, "description":fms_id[0:8]} + # If enabled, look up description + if globals.config.getint("FMS", "idDescribed"): + from includes import descriptionList + data["description"] = descriptionList.getDescription("FMS", fms_id[0:8]) + # processing the alarm from includes import alarmHandler alarmHandler.processAlarm("FMS",freq,data) diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index 3aab240..f40bc40 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -110,9 +110,14 @@ def decode(freq, decoded): globals.poc_time_old = timestamp else: 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} + data = {"ric":poc_id, "function":poc_sub, "msg":poc_text, "bitrate":bitrate, "description":poc_id} # Add function as character a-d to dataset data["functionChar"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") + # If enabled, look up description + if globals.config.getint("POC", "idDescribed"): + from includes import descriptionList + data["description"] = descriptionList.getDescription("POC", poc_id) + # processing the alarm from includes import alarmHandler alarmHandler.processAlarm("POC",freq,data) diff --git a/includes/decoders/zvei.py b/includes/decoders/zvei.py index 1d87f12..8d24ac0 100644 --- a/includes/decoders/zvei.py +++ b/includes/decoders/zvei.py @@ -44,7 +44,12 @@ def decode(freq, decoded): globals.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} + data = {"zvei":zvei_id, "description":zvei_id} + # If enabled, look up description + if globals.config.getint("ZVEI", "idDescribed"): + from includes import descriptionList + data["description"] = descriptionList.getDescription("ZVEI", zvei_id) + # processing the alarm from includes import alarmHandler alarmHandler.processAlarm("ZVEI",freq,data) diff --git a/includes/descriptionList.py b/includes/descriptionList.py new file mode 100644 index 0000000..c65c091 --- /dev/null +++ b/includes/descriptionList.py @@ -0,0 +1,102 @@ +#!/usr/bin/python +# -*- coding: cp1252 -*- + +""" +Function to expand the dataset with a description. + +@author: Jens Herrmann + +@requires: Configuration has to be set in the config.ini +""" + +import logging # Global logger + +import csv + +from includes import globals # Global variables + +## +# +# Local function will load the csv-file +# +def loadCSV(typ, idField): + """ + Local function for loading csv-file into python list + Structure: [id] = description + + @return: Python list of descriptions + @exception: Exception if loading failed + """ + resultList = {} + try: + logging.debug("-- loading %s.csv", typ) + with open(globals.script_path+'/csv/'+typ+'.csv') as csvfile: + # DictReader expected structure described in first line of csv-file + reader = csv.DictReader(csvfile) + for row in reader: + logging.debug(row) + # only import rows with an integer as id + if row[idField].isdigit() == True: + resultList[row[idField]] = row['description'] + logging.debug("-- loading csv finished") + except: + logging.exception("loading csvList for typ: %s failed", typ) + return resultList; + +## +# +# call this for loading the description lists +# +def loadDescriptionLists(): + """ + Load data from the csv-files in global description list for FMS, ZVEI and POCSAG + + @return: nothing + @exception: Exception if loading failed + """ + try: + logging.debug("loading description lists") + + if globals.config.getint("FMS", "idDescribed"): + logging.debug("- load FMS description list") + globals.fmsDescribtionList = loadCSV("fms", "fms") + + if globals.config.getint("ZVEI", "idDescribed"): + logging.debug("- load ZVEI description list") + globals.zveiDescribtionList = loadCSV("zvei", "zvei") + + if globals.config.getint("POC", "idDescribed"): + logging.debug("- load pocsag description list") + globals.ricDescribtionList = loadCSV("poc", "ric") + + except: + logging.exception("cannot load description lists") + + +## +# +# public function for getting a description +# +def getDescription(typ, id): + """ + Get description for id. + Will return id if no description will be found. + + @return: description as string + """ + resultStr = id; + logging.debug("look up description lists") + try: + if typ == "FMS": + resultStr = globals.fmsDescribtionList[id] + elif typ == "ZVEI": + resultStr = globals.zveiDescribtionList[id] + elif typ == "POC": + resultStr = globals.ricDescribtionList[id] + else: + logging.warning("Invalid Typ: %s", typ) + except KeyError: + # will be thrown when there is no description for the id + # -> nothing to do... + pass + return resultStr \ No newline at end of file diff --git a/includes/globals.py b/includes/globals.py index b96e81b..46aa5fc 100644 --- a/includes/globals.py +++ b/includes/globals.py @@ -26,4 +26,10 @@ poc_time_old = 0 pluginList = {} #filter -filterList = [] \ No newline at end of file +filterList = [] + +#idDescribing +fmsDescribtionList = {} +zveiDescribtionList = {} +ricDescribtionList = {} + diff --git a/plugins/eMail/eMail.py b/plugins/eMail/eMail.py index 842d013..24477a5 100644 --- a/plugins/eMail/eMail.py +++ b/plugins/eMail/eMail.py @@ -125,12 +125,14 @@ def run(typ,freq,data): subject = subject.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards subject = subject.replace("%DIR%", data["direction"]).replace("%DIRT%", data["directionText"]) #replace Wildcards subject = subject.replace("%TSI%", data["tsi"]) #replace Wildcards + subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards subject = subject.replace("%TIME%", curtime()) # replace Wildcards # read mailtext-structure from config.ini mailtext = globals.config.get("eMail", "fms_message") mailtext = mailtext.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards mailtext = mailtext.replace("%DIR%", data["direction"]).replace("%DIRT%", data["directionText"]) #replace Wildcards mailtext = mailtext.replace("%TSI%", data["tsi"]) #replace Wildcards + mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards # send eMail doSendmail(server, subject, mailtext) @@ -143,10 +145,12 @@ def run(typ,freq,data): # read subject-structure from config.ini subject = globals.config.get("eMail", "zvei_subject") subject = subject.replace("%ZVEI%", data["zvei"]) #replace Wildcards + subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards subject = subject.replace("%TIME%", curtime()) # replace Wildcards # read mailtext-structure from config.ini mailtext = globals.config.get("eMail", "zvei_message") mailtext = mailtext.replace("%ZVEI%", data["zvei"]) #replace Wildcards + mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards # send eMail doSendmail(server, subject, mailtext) @@ -161,12 +165,14 @@ def run(typ,freq,data): subject = subject.replace("%RIC%", data["ric"]) #replace Wildcards subject = subject.replace("%FUNC%", data["function"]).replace("%FUNCCHAR%", data["functionChar"]) #replace Wildcards subject = subject.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards + subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards subject = subject.replace("%TIME%", curtime()) # replace Wildcards # read mailtext-structure from config.ini mailtext = globals.config.get("eMail", "poc_message") mailtext = mailtext.replace("%RIC%", data["ric"]) #replace Wildcards mailtext = mailtext.replace("%FUNC%", data["function"]).replace("%FUNCCHAR%", data["functionChar"]) #replace Wildcards mailtext = mailtext.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards + mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards # send eMail doSendmail(server, subject, mailtext) diff --git a/plugins/interface.txt b/plugins/interface.txt index 821729d..f2e8c01 100644 --- a/plugins/interface.txt +++ b/plugins/interface.txt @@ -9,6 +9,7 @@ They can be used by their Index Names: data['OPTION'] ZVEI: - zvei +- description FMS: - fms @@ -16,6 +17,7 @@ FMS: - direction - directionText - tsi +- description POCSAG: - ric @@ -23,6 +25,7 @@ POCSAG: - functionChar - msg - bitrate +- description Global Objects: