diff --git a/bridge_gps_data.py b/bridge_gps_data.py index ff8fef7..72e9abe 100755 --- a/bridge_gps_data.py +++ b/bridge_gps_data.py @@ -162,11 +162,11 @@ def aprs_send(packet): def dashboard_loc_write(call, lat, lon, time, comment): #try: - dash_entries = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read()) + dash_entries = ast.literal_eval(os.popen('cat ' + loc_file).read()) # except: # dash_entries = [] dash_entries.insert(0, {'call': call, 'lat': lat, 'lon': lon, 'time':time, 'comment': comment}) - with open("/tmp/gps_data_user_loc.txt", 'w') as user_loc_file: + with open(loc_file, 'w') as user_loc_file: user_loc_file.write(str(dash_entries[:200])) user_loc_file.close() logger.info('User location saved for dashboard') @@ -174,11 +174,11 @@ def dashboard_loc_write(call, lat, lon, time, comment): def dashboard_bb_write(call, dmr_id, time, bulletin): #try: - dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').read()) + dash_bb = ast.literal_eval(os.popen('cat ' + bb_file).read()) # except: # dash_entries = [] dash_bb.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'bulletin':bulletin}) - with open("/tmp/gps_data_user_bb.txt", 'w') as user_bb_file: + with open(bb_file, 'w') as user_bb_file: user_bb_file.write(str(dash_bb[:20])) user_bb_file.close() logger.info('User bulletin entry saved.') @@ -186,29 +186,28 @@ def dashboard_bb_write(call, dmr_id, time, bulletin): def mailbox_write(call, dmr_id, time, message, recipient): #try: - mail_file = ast.literal_eval(os.popen('cat ./gps_data_user_mailbox.txt').read()) + mail_file = ast.literal_eval(os.popen('cat ' + the_mailbox_file).read()) mail_file.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'message':message, 'recipient': recipient}) - with open("./gps_data_user_mailbox.txt", 'w') as mailbox_file: + with open(the_mailbox_file, 'w') as mailbox_file: mailbox_file.write(str(mail_file[:100])) mailbox_file.close() logger.info('User mail saved.') def mailbox_delete(dmr_id): - mail_file = ast.literal_eval(os.popen('cat ./gps_data_user_mailbox.txt').read()) + mail_file = ast.literal_eval(os.popen('cat ' + the_mailbox_file).read()) call = str(get_alias((dmr_id), subscriber_ids)) new_data = [] for message in mail_file: if message['recipient'] != call: new_data.append(message) - with open("./gps_data_user_mailbox.txt", 'w') as mailbox_file: + with open(the_mailbox_file, 'w') as mailbox_file: mailbox_file.write(str(new_data[:100])) mailbox_file.close() logger.info('Mailbox updated. Delete occurred.') - def sos_write(dmr_id, time, message): - user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read()) + user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read()) try: if user_settings[dmr_id][1]['ssid'] == '': sos_call = user_settings[dmr_id][0]['call'] + '-' + user_ssid @@ -216,9 +215,8 @@ def sos_write(dmr_id, time, message): sos_call = user_settings[dmr_id][0]['call'] + '-' + user_settings[dmr_id][1]['ssid'] except: sos_call = str(get_alias((dmr_id), subscriber_ids)) - sos_info = {'call': sos_call, 'dmr_id': dmr_id, 'time': time, 'message':message} - with open("/tmp/gps_data_user_sos.txt", 'w') as sos_file: + with open(emergency_sos_file, 'w') as sos_file: sos_file.write(str(sos_info)) sos_file.close() logger.info('Saved SOS.') @@ -261,6 +259,8 @@ def user_setting_write(dmr_id, setting, value): user_dict[dmr_id][1]['ssid'] = value if setting.upper() == 'COM': user_comment = user_dict[dmr_id][3]['comment'] = value[0:35] + if setting.upper() == 'APRS': + user_dict[dmr_id] = [{'call': str(get_alias((dmr_id), subscriber_ids))}, {'ssid': ''}, {'icon': ''}, {'comment': ''}] f.close() logger.info('Loaded user settings. Preparing to write...') # Write modified dict to file @@ -288,6 +288,9 @@ def process_sms(_rf_src, sms): user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), re.sub('@SSID| ','',sms)) elif '@COM' in sms: user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), re.sub('@COM |@COM','',sms)) + # Write blank entry to cause APRS receive to look for packets for this station. + elif '@APRS' in sms: + user_setting_write(int_id(_rf_src), 'APRS', '') elif '@BB' in sms: dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time(), re.sub('@BB|@BB ','',sms)) elif '@' and ' E-' in sms: @@ -306,7 +309,7 @@ def process_sms(_rf_src, sms): elif '@SOS' in sms or '@NOTICE' in sms: sos_write(int_id(_rf_src), time(), sms) elif '@REM SOS' == sms: - os.remove('/tmp/gps_data_user_sos.txt') + os.remove(emergency_sos_file) logger.info('Removing SOS') elif '@' and 'M-' in sms: message = re.sub('^@|.* M-|','',sms) @@ -358,7 +361,7 @@ def process_sms(_rf_src, sms): ssid = user_settings[int_id(_rf_src)][1]['ssid'] if user_settings[int_id(_rf_src)][3]['comment'] != '': comment = user_settings[int_id(_rf_src)][3]['comment'] - aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment) + aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment) logger.info(aprs_loc_packet) logger.info('User comment: ' + comment) logger.info('User SSID: ' + ssid) @@ -1539,7 +1542,7 @@ class routerHBP(HBSYSTEM): ssid = user_settings[int_id(_rf_src)][1]['ssid'] if user_settings[int_id(_rf_src)][3]['comment'] != '': comment = user_settings[int_id(_rf_src)][3]['comment'] - aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment) + aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment) logger.info(aprs_loc_packet) logger.info('User comment: ' + comment) logger.info('User SSID: ' + ssid) @@ -1635,14 +1638,14 @@ class routerHBP(HBSYSTEM): ssid = user_settings[int_id(_rf_src)][1]['ssid'] if user_settings[int_id(_rf_src)][3]['comment'] != '': comment = user_settings[int_id(_rf_src)][3]['comment'] - aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment) + aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment) logger.info(aprs_loc_packet) logger.info('User comment: ' + comment) logger.info('User SSID: ' + ssid) logger.info('User icon: ' + icon_table + icon_icon) except Exception as error_exception: logger.info('Error or user settings file not found, proceeding with default settings.') - aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + str(user_ssid) + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + '/' + str(loc.lon[0:8]) + str(loc.lon_dir) + '[' + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + aprs_comment + ' DMR ID: ' + str(int_id(_rf_src)) + aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + str(user_ssid) + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + '/' + str(loc.lon[0:8]) + str(loc.lon_dir) + '[' + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + aprs_comment + ' DMR ID: ' + str(int_id(_rf_src)) logger.info(error_exception) logger.info(str(traceback.extract_tb(error_exception.__traceback__))) try: @@ -1769,38 +1772,6 @@ if __name__ == '__main__': # Change the current directory to the location of the application os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) - # Check if user_settings (for APRS settings of users) exists. Creat it if not. - if Path('./user_settings.txt').is_file(): - pass - else: - Path('./user_settings.txt').touch() - with open("./user_settings.txt", 'w') as user_dict_file: - user_dict_file.write("{1: [{'call': 'N0CALL'}, {'ssid': ''}, {'icon': ''}, {'comment': ''}]}") - user_dict_file.close() - # Check to see if dashboard files exist - if Path('/tmp/gps_data_user_loc.txt').is_file(): - pass - else: - Path('/tmp/gps_data_user_loc.txt').touch() - with open("/tmp/gps_data_user_loc.txt", 'w') as user_loc_file: - user_loc_file.write("[]") - user_loc_file.close() - if Path('/tmp/gps_data_user_bb.txt').is_file(): - pass - else: - Path('/tmp/gps_data_user_bb.txt').touch() - with open("/tmp/gps_data_user_bb.txt", 'w') as user_bb_file: - user_bb_file.write("[]") - user_bb_file.close() - if Path('./gps_data_user_mailbox.txt').is_file(): - pass - else: - Path('./gps_data_user_mailbox.txt').touch() - with open("./gps_data_user_mailbox.txt", 'w') as user_loc_file: - user_loc_file.write("[]") - user_loc_file.close() - - # CLI argument parser - handles picking up the config file from the command line, and sending a "help" message parser = argparse.ArgumentParser() parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually hblink.cfg)') @@ -1833,6 +1804,43 @@ if __name__ == '__main__': smtp_server = CONFIG['GPS_DATA']['SMTP_SERVER'] smtp_port = CONFIG['GPS_DATA']['SMTP_PORT'] + # Dashboard files + bb_file = CONFIG['GPS_DATA']['BULLETIN_BOARD_FILE'] + loc_file = CONFIG['GPS_DATA']['LOCATION_FILE'] + the_mailbox_file = CONFIG['GPS_DATA']['MAILBOX_FILE'] + emergency_sos_file = CONFIG['GPS_DATA']['EMERGENCY_SOS_FILE'] + + # Check if user_settings (for APRS settings of users) exists. Creat it if not. + if Path('./user_settings.txt').is_file(): + pass + else: + Path('./user_settings.txt').touch() + with open("./user_settings.txt", 'w') as user_dict_file: + user_dict_file.write("{1: [{'call': 'N0CALL'}, {'ssid': ''}, {'icon': ''}, {'comment': ''}]}") + user_dict_file.close() + # Check to see if dashboard files exist + if Path(loc_file).is_file(): + pass + else: + Path(loc_file).touch() + with open(loc_file, 'w') as user_loc_file: + user_loc_file.write("[]") + user_loc_file.close() + if Path(bb_file).is_file(): + pass + else: + Path(bb_file).touch() + with open(bb_file, 'w') as user_bb_file: + user_bb_file.write("[]") + user_bb_file.close() + if Path(the_mailbox_file).is_file(): + pass + else: + Path(the_mailbox_file).touch() + with open(the_mailbox_file, 'w') as user_loc_file: + user_loc_file.write("[]") + user_loc_file.close() + # Ensure we have a path for the rules file, if one wasn't specified, then use the default (top of file) if not cli_args.RULES_FILE: cli_args.RULES_FILE = os.path.dirname(os.path.abspath(__file__))+'/rules.py' diff --git a/config.py b/config.py index fb97eab..f8ed618 100755 --- a/config.py +++ b/config.py @@ -161,6 +161,10 @@ def build_config(_config_file): 'EMAIL_PASSWORD': config.get(section, 'EMAIL_PASSWORD'), 'SMTP_SERVER': config.get(section, 'SMTP_SERVER'), 'SMTP_PORT': config.get(section, 'SMTP_PORT'), + 'LOCATION_FILE': config.get(section, 'LOCATION_FILE'), + 'BULLETIN_BOARD_FILE': config.get(section, 'BULLETIN_BOARD_FILE'), + 'MAILBOX_FILE': config.get(section, 'MAILBOX_FILE'), + 'EMERGENCY_SOS_FILE': config.get(section, 'EMERGENCY_SOS_FILE'), }) if not CONFIG['LOGGER']['LOG_FILE']: diff --git a/gps_data-SAMPLE.cfg b/gps_data-SAMPLE.cfg index 9fa1fcf..046248e 100644 --- a/gps_data-SAMPLE.cfg +++ b/gps_data-SAMPLE.cfg @@ -105,6 +105,7 @@ PEER_URL: https://www.radioid.net/static/rptrs.json SUBSCRIBER_URL: https://www.radioid.net/static/users.json STALE_DAYS: 1 +##################################################################### # GPS/Data Application - by KF7EEL # Configure the settings for the DMR GPS to APRS position application here. # @@ -126,6 +127,11 @@ APRS_LOGIN_PASSCODE: 12345 APRS_SERVER: rotate.aprs2.net APRS_PORT: 14580 +# The APRS filter below is used for the message received script. See http://www.aprs-is.net/javAPRSFilter.aspx for details +# about APRS filters. +APRS_RECEIVE_LOGIN_CALL: N0CALL-1 +APRS_FILTER: r/47/-120/500 t/m + # The following settings are only applicable if you are using the gps_data_beacon_igate script. # They do not affect the operation gps_data itself. # Time in minutes. @@ -135,13 +141,66 @@ IGATE_BEACON_ICON = /I IGATE_LATITUDE = 0000.00N IGATE_LONGITUDE = 00000.00W +# The options below are required for operation of the dashboard. Leave them as default +# unless you know what you are doing. +LOCATION_FILE: /tmp/gps_data_user_loc.txt +BULLETIN_BOARD_FILE: /tmp/gps_data_user_bb.txt +MAILBOX_FILE: /tmp/gps_data_user_mailbox.txt +EMERGENCY_SOS_FILE: /tmp/gps_data_user_sos.txt + # The email gateway settingns below are OPTIONAL. They are NOT REQUIRED if you don't want -# to enable the email gateway. Leave as is to disable. +http://www.aprs-is.net/javAPRSFilter.aspx# to enable the email gateway. Leave as is to disable. EMAIL_SENDER: test@example.org EMAIL_PASSWORD: letmein SMTP_SERVER: smtp.gmail.com SMTP_PORT: 465 +# The options below are required for operation of the dashboard and will cause errors in gps_data.py +# if configured wrong. Leave them as default unless you know what you are doing. +# If you do change, you must use absolute paths. +LOCATION_FILE: /tmp/gps_data_user_loc.txt +BULLETIN_BOARD_FILE: /tmp/gps_data_user_bb.txt +MAILBOX_FILE: /tmp/gps_data_user_mailbox.txt +EMERGENCY_SOS_FILE: /tmp/gps_data_user_sos.txt + +# The following options are used for the dashboard. The dashboard is optional. +# Title of the Dashboard +DASHBOARD_TITLE: HBLink3 D-APRS Dashboard + +# Logo used on dashboard page +LOGO: https://raw.githubusercontent.com/kf7eel/hblink3/gps/HBlink.png + +# Port to run server +DASH_PORT: 8092 + +# IP to run server on +DASH_HOST: 127.0.0.1 + +#Description of dashboard to show on main page +DESCRIPTION: Welcome to the dashboard. + +# Gateway contact info displayed on about page. +CONTACT_NAME: your name +CONTACT_CALL: N0CALL +CONTACT_EMAIL: email@example.org +CONTACT_WEBSITE: https://hbl.ink + +# Time format for display +TIME_FORMAT: %%H:%%M:%%S - %%m/%%d/%%y + +# Center dashboard map over these coordinates +MAP_CENTER_LAT: 47.00 +MAP_CENTER_LON: -120.00 +ZOOM_LEVEL: 7 + +# List and preview of some map themes at http://leaflet-extras.github.io/leaflet-providers/preview/ +# The following are options for map themes and just work, you should use one of these: “OpenStreetMap”, “Stamen” (Terrain, Toner, and Watercolor), +MAP_THEME: Stamen Toner + +# RSS feed link, shows in the link section of each RSS item. +RSS_LINK: http://localhost:8092 + +##################################################################### # OPENBRIDGE INSTANCES - DUPLICATE SECTION FOR MULTIPLE CONNECTIONS # OpenBridge is a protocol originall created by DMR+ for connection between an # IPSC2 server and Brandmeister. It has been implemented here at the suggestion diff --git a/gps_data.py b/gps_data.py index 7068327..2cb73bf 100644 --- a/gps_data.py +++ b/gps_data.py @@ -155,11 +155,11 @@ def aprs_send(packet): def dashboard_loc_write(call, lat, lon, time, comment): #try: - dash_entries = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read()) + dash_entries = ast.literal_eval(os.popen('cat ' + loc_file).read()) # except: # dash_entries = [] dash_entries.insert(0, {'call': call, 'lat': lat, 'lon': lon, 'time':time, 'comment': comment}) - with open("/tmp/gps_data_user_loc.txt", 'w') as user_loc_file: + with open(loc_file, 'w') as user_loc_file: user_loc_file.write(str(dash_entries[:200])) user_loc_file.close() logger.info('User location saved for dashboard') @@ -167,11 +167,11 @@ def dashboard_loc_write(call, lat, lon, time, comment): def dashboard_bb_write(call, dmr_id, time, bulletin): #try: - dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').read()) + dash_bb = ast.literal_eval(os.popen('cat ' + bb_file).read()) # except: # dash_entries = [] dash_bb.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'bulletin':bulletin}) - with open("/tmp/gps_data_user_bb.txt", 'w') as user_bb_file: + with open(bb_file, 'w') as user_bb_file: user_bb_file.write(str(dash_bb[:20])) user_bb_file.close() logger.info('User bulletin entry saved.') @@ -179,21 +179,21 @@ def dashboard_bb_write(call, dmr_id, time, bulletin): def mailbox_write(call, dmr_id, time, message, recipient): #try: - mail_file = ast.literal_eval(os.popen('cat ./gps_data_user_mailbox.txt').read()) + mail_file = ast.literal_eval(os.popen('cat ' + the_mailbox_file).read()) mail_file.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'message':message, 'recipient': recipient}) - with open("./gps_data_user_mailbox.txt", 'w') as mailbox_file: + with open(the_mailbox_file, 'w') as mailbox_file: mailbox_file.write(str(mail_file[:100])) mailbox_file.close() logger.info('User mail saved.') def mailbox_delete(dmr_id): - mail_file = ast.literal_eval(os.popen('cat ./gps_data_user_mailbox.txt').read()) + mail_file = ast.literal_eval(os.popen('cat ' + the_mailbox_file).read()) call = str(get_alias((dmr_id), subscriber_ids)) new_data = [] for message in mail_file: if message['recipient'] != call: new_data.append(message) - with open("./gps_data_user_mailbox.txt", 'w') as mailbox_file: + with open(the_mailbox_file, 'w') as mailbox_file: mailbox_file.write(str(new_data[:100])) mailbox_file.close() logger.info('Mailbox updated. Delete occurred.') @@ -209,7 +209,7 @@ def sos_write(dmr_id, time, message): except: sos_call = str(get_alias((dmr_id), subscriber_ids)) sos_info = {'call': sos_call, 'dmr_id': dmr_id, 'time': time, 'message':message} - with open("/tmp/gps_data_user_sos.txt", 'w') as sos_file: + with open(emergency_sos_file, 'w') as sos_file: sos_file.write(str(sos_info)) sos_file.close() logger.info('Saved SOS.') @@ -252,6 +252,8 @@ def user_setting_write(dmr_id, setting, value): user_dict[dmr_id][1]['ssid'] = value if setting.upper() == 'COM': user_comment = user_dict[dmr_id][3]['comment'] = value[0:35] + if setting.upper() == 'APRS': + user_dict[dmr_id] = [{'call': str(get_alias((dmr_id), subscriber_ids))}, {'ssid': ''}, {'icon': ''}, {'comment': ''}] f.close() logger.info('Loaded user settings. Preparing to write...') # Write modified dict to file @@ -279,6 +281,9 @@ def process_sms(_rf_src, sms): user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), re.sub('@SSID| ','',sms)) elif '@COM' in sms: user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), re.sub('@COM |@COM','',sms)) + # Write blank entry to cause APRS receive to look for packets for this station. + elif '@APRS' in sms: + user_setting_write(int_id(_rf_src), 'APRS', '') elif '@BB' in sms: dashboard_bb_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.time(), re.sub('@BB|@BB ','',sms)) elif '@' and ' E-' in sms: @@ -297,7 +302,7 @@ def process_sms(_rf_src, sms): elif '@SOS' in sms or '@NOTICE' in sms: sos_write(int_id(_rf_src), time.time(), sms) elif '@REM SOS' == sms: - os.remove('/tmp/gps_data_user_sos.txt') + os.remove(emergency_sos_file) logger.info('Removing SOS or Notice') elif '@' and 'M-' in sms: message = re.sub('^@|.* M-|','',sms) @@ -349,7 +354,7 @@ def process_sms(_rf_src, sms): ssid = user_settings[int_id(_rf_src)][1]['ssid'] if user_settings[int_id(_rf_src)][3]['comment'] != '': comment = user_settings[int_id(_rf_src)][3]['comment'] - aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment) + aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment) logger.info(aprs_loc_packet) logger.info('User comment: ' + comment) logger.info('User SSID: ' + ssid) @@ -485,7 +490,7 @@ class DATA_SYSTEM(HBSYSTEM): ssid = user_settings[int_id(_rf_src)][1]['ssid'] if user_settings[int_id(_rf_src)][3]['comment'] != '': comment = user_settings[int_id(_rf_src)][3]['comment'] - aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment) + aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(aprs_lat) + icon_table + str(aprs_lon) + icon_icon + '/' + str(comment) logger.info(aprs_loc_packet) logger.info('User comment: ' + comment) logger.info('User SSID: ' + ssid) @@ -581,14 +586,14 @@ class DATA_SYSTEM(HBSYSTEM): ssid = user_settings[int_id(_rf_src)][1]['ssid'] if user_settings[int_id(_rf_src)][3]['comment'] != '': comment = user_settings[int_id(_rf_src)][3]['comment'] - aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment) + aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment) logger.info(aprs_loc_packet) logger.info('User comment: ' + comment) logger.info('User SSID: ' + ssid) logger.info('User icon: ' + icon_table + icon_icon) except Exception as error_exception: logger.info('Error or user settings file not found, proceeding with default settings.') - aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + str(user_ssid) + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + '/' + str(loc.lon[0:8]) + str(loc.lon_dir) + '[' + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + aprs_comment + ' DMR ID: ' + str(int_id(_rf_src)) + aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + str(user_ssid) + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + '/' + str(loc.lon[0:8]) + str(loc.lon_dir) + '[' + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + aprs_comment + ' DMR ID: ' + str(int_id(_rf_src)) logger.info(error_exception) logger.info(str(traceback.extract_tb(error_exception.__traceback__))) try: @@ -671,36 +676,7 @@ if __name__ == '__main__': # Change the current directory to the location of the application os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) - # Check if user_settings (for APRS settings of users) exists. Creat it if not. - if Path('./user_settings.txt').is_file(): - pass - else: - Path('./user_settings.txt').touch() - with open("./user_settings.txt", 'w') as user_dict_file: - user_dict_file.write("{1: [{'call': 'N0CALL'}, {'ssid': ''}, {'icon': ''}, {'comment': ''}]}") - user_dict_file.close() - # Check to see if dashboard files exist - if Path('/tmp/gps_data_user_loc.txt').is_file(): - pass - else: - Path('/tmp/gps_data_user_loc.txt').touch() - with open("/tmp/gps_data_user_loc.txt", 'w') as user_loc_file: - user_loc_file.write("[]") - user_loc_file.close() - if Path('/tmp/gps_data_user_bb.txt').is_file(): - pass - else: - Path('/tmp/gps_data_user_bb.txt').touch() - with open("/tmp/gps_data_user_bb.txt", 'w') as user_bb_file: - user_bb_file.write("[]") - user_bb_file.close() - if Path('./gps_data_user_mailbox.txt').is_file(): - pass - else: - Path('./gps_data_user_mailbox.txt').touch() - with open("./gps_data_user_mailbox.txt", 'w') as user_loc_file: - user_loc_file.write("[]") - user_loc_file.close() + # CLI argument parser - handles picking up the config file from the command line, and sending a "help" message parser = argparse.ArgumentParser() parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually gps_data.cfg)') @@ -730,6 +706,43 @@ if __name__ == '__main__': email_password = CONFIG['GPS_DATA']['EMAIL_PASSWORD'] smtp_server = CONFIG['GPS_DATA']['SMTP_SERVER'] smtp_port = CONFIG['GPS_DATA']['SMTP_PORT'] + + # Dashboard files + bb_file = CONFIG['GPS_DATA']['BULLETIN_BOARD_FILE'] + loc_file = CONFIG['GPS_DATA']['LOCATION_FILE'] + the_mailbox_file = CONFIG['GPS_DATA']['MAILBOX_FILE'] + emergency_sos_file = CONFIG['GPS_DATA']['EMERGENCY_SOS_FILE'] + + # Check if user_settings (for APRS settings of users) exists. Creat it if not. + if Path('./user_settings.txt').is_file(): + pass + else: + Path('./user_settings.txt').touch() + with open("./user_settings.txt", 'w') as user_dict_file: + user_dict_file.write("{1: [{'call': 'N0CALL'}, {'ssid': ''}, {'icon': ''}, {'comment': ''}]}") + user_dict_file.close() + # Check to see if dashboard files exist + if Path(loc_file).is_file(): + pass + else: + Path(loc_file).touch() + with open(loc_file, 'w') as user_loc_file: + user_loc_file.write("[]") + user_loc_file.close() + if Path(bb_file).is_file(): + pass + else: + Path(bb_file).touch() + with open(bb_file, 'w') as user_bb_file: + user_bb_file.write("[]") + user_bb_file.close() + if Path(the_mailbox_file).is_file(): + pass + else: + Path(the_mailbox_file).touch() + with open(the_mailbox_file, 'w') as user_loc_file: + user_loc_file.write("[]") + user_loc_file.close() # Start the system logger if cli_args.LOG_LEVEL: diff --git a/scripts/aprs_receive/receive.py b/scripts/aprs_receive/receive.py index 66dd419..991d0da 100644 --- a/scripts/aprs_receive/receive.py +++ b/scripts/aprs_receive/receive.py @@ -1,28 +1,83 @@ +############################################################################### +# GPS/Data - Copyright (C) 2020 Eric Craw, KF7EEL +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +############################################################################### + import aprslib import ast, os +import re +from configparser import ConfigParser +import time +import argparse def mailbox_write(call, dmr_id, time, message, recipient): - mail_file = ast.literal_eval(os.popen('cat ../../gps_data_user_mailbox.txt').read()) + mail_file = ast.literal_eval(os.popen('cat ' + mailbox_file).read()) mail_file.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'message':message, 'recipient': recipient}) with open("../../gps_data_user_mailbox.txt", 'w') as mailbox_file: mailbox_file.write(str(mail_file[:100])) mailbox_file.close() - logger.info('User mail saved.') + print('User mail saved.') def aprs_filter(packet): #if aprslib.parse(packet) in aprslib.parse(packet): # print(aprslib.parse(packet)) #else: # pass - if aprslib.parse(packet)['to'] in user_settings: - print(aprslib.parse(packet)) - mailbox_write(aprslib.parse(packet)['from'], aprslib.parse(packet)['to'], time.time(), aprslib.parse(packet)['message_text'], recipient) - -user_settings = ast.literal_eval(os.popen('cat ../../user_settings.txt').read()) -recipient = re.sub('-.*','', aprslib.parse(packet)['to']) -AIS = aprslib.IS("N0CALL", host='rotate.aprs.net') -AIS.connect() -# by default `raw` is False, then each line is ran through aprslib.parse() -AIS.consumer(aprs_filter, raw=True) + user_settings = ast.literal_eval(os.popen('cat ../../user_settings.txt').read()) + if 'addresse' in aprslib.parse(packet): + #print(aprslib.parse(packet)) + recipient = re.sub('-.*','', aprslib.parse(packet)['addresse']) + recipient_ssid = re.sub('.*-','', aprslib.parse(packet)['addresse']) + + for i in user_settings.items(): +## print('checking user_settings ' + recipient) + if recipient in i[1][0]['call'] and recipient_ssid in i[1][1]['ssid']: +## print(i[1][0]) +## print(i[1][1]) +## print(aprslib.parse(packet)) + mailbox_write(re.sub('-.*','', aprslib.parse(packet)['addresse']), aprslib.parse(packet)['from'], time.time(), aprslib.parse(packet)['message_text'], recipient) + if 'msgNo' in aprslib.parse(packet): + time.sleep(1) + AIS.sendall(aprslib.parse(packet)['addresse'] + '>APHBL3,TCPIP*:' + ':' + aprslib.parse(packet)['from'].ljust(9) +':ack'+aprslib.parse(packet)['msgNo']) + print('Send ACK') + print(aprslib.parse(packet)['addresse'] + '>APHBL3,TCPIP*:' + ':' + aprslib.parse(packet)['from'].ljust(9) +':ack'+aprslib.parse(packet)['msgNo']) +## else: +## print(aprslib.parse(packet)['from']) + +if __name__ == '__main__': + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually gps_data.cfg)') + cli_args = arg_parser.parse_args() + parser = ConfigParser() + if not cli_args.CONFIG_FILE: + print('\n\nMust specify a config file with -c argument.\n\n') + parser.read(cli_args.CONFIG_FILE) + + aprs_server = parser.get('GPS_DATA', 'APRS_SERVER') + aprs_port = parser.get('GPS_DATA', 'APRS_PORT') + aprs_login = parser.get('GPS_DATA', 'APRS_RECEIVE_LOGIN_CALL') + aprs_passcode = parser.get('GPS_DATA', 'APRS_LOGIN_PASSCODE') + mailbox_file = parser.get('GPS_DATA', 'MAILBOX_FILE') + + AIS = aprslib.IS(aprs_login, passwd=int(aprs_passcode), host=aprs_server, port=int(aprs_port)) + user_settings = ast.literal_eval(os.popen('cat ../../user_settings.txt').read()) + print('APRS message receive script for GPS/Data Application.\nAuthor: Eric, KF7EEL - kf7eel@qsl.net') + AIS.set_filter(parser.get('GPS_DATA', 'APRS_FILTER')) + AIS.connect() + print('Connecting to APRS-IS') + AIS.consumer(aprs_filter, raw=True) diff --git a/scripts/dashboard/dashboard.py b/scripts/dashboard/dashboard.py index b9b75d5..e3fd1c5 100644 --- a/scripts/dashboard/dashboard.py +++ b/scripts/dashboard/dashboard.py @@ -23,11 +23,15 @@ This is a web dashboard for the GPS/Data application. from flask import Flask, render_template, request, Response, Markup import ast, os -from dashboard_settings import * +#from dashboard_settings import * import folium from folium.plugins import MarkerCluster import re from datetime import datetime +import argparse +from configparser import ConfigParser + + app = Flask(__name__) @@ -42,7 +46,7 @@ tbl_ftr = ''' def get_loc_data(): try: - dash_loc = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read()) + dash_loc = ast.literal_eval(os.popen('cat ' + loc_file).read()) tmp_loc = '' loc_hdr = ''' @@ -88,7 +92,7 @@ def get_loc_data(): def get_bb_data(): try: - dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').read()) + dash_bb = ast.literal_eval(os.popen('cat ' + bb_file).read()) tmp_bb = '' bb_hdr = ''' @@ -132,7 +136,7 @@ def get_bb_data(): def check_emergency(): # open emergency txt try: - sos_file = ast.literal_eval(os.popen('cat /tmp/gps_data_user_sos.txt').read()) + sos_file = ast.literal_eval(os.popen('cat ' + emergency_sos_file).read()) if type(sos_file['time']) == str: loc_time = str(sos_file['time']) if type(sos_file['time']) == int or type(sos_file['time']) == float: @@ -200,7 +204,7 @@ def view_map(): reload_time = request.args.get('reload') track_call = request.args.get('track') map_size = request.args.get('map_size') - user_loc = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read()) + user_loc = ast.literal_eval(os.popen('cat ' + loc_file).read()) last_known_list = [] try: if track_call: @@ -302,7 +306,7 @@ def view_map(): return render_template('generic.html', title = dashboard_title, logo = logo, content = Markup(content)) if not track_call: - folium_map = folium.Map(location=map_center, tiles=map_theme, zoom_start=int(zoom_level)) + folium_map = folium.Map(location=(map_center_lat, map_center_lon), tiles=map_theme, zoom_start=int(zoom_level)) marker_cluster = MarkerCluster().add_to(folium_map) for user_coord in user_loc: user_lat = aprs_to_latlon(float(re.sub('[A-Za-z]','', user_coord['lat']))) @@ -406,7 +410,7 @@ def user_settings(): if icon == '': icon = '\[' if comment == '': - comment = 'Default comment.' + comment = default_comment + ' ' + user_id #for result in user_settings: #return user_settings[int(user_id)][0] #return user_id @@ -471,7 +475,7 @@ def mailbox(): """ else: - mailbox_file = ast.literal_eval(os.popen('cat ../../gps_data_user_mailbox.txt').read()) + mailbox_file = ast.literal_eval(os.popen('cat ' + the_mailbox_file).read()) mail_content = '

Messages for: ' + recipient.upper() + '''

\n

\n

Mailbox RSS Feed for ''' + recipient.upper() + '''

@@ -518,10 +522,11 @@ def mailbox(): """ return render_template('generic.html', title = dashboard_title, logo = logo, content = Markup(mail_content)) + @app.route('/bulletin_rss.xml') def bb_rss(): try: - dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').read()) + dash_bb = ast.literal_eval(os.popen('cat ' + bb_file).read()) post_data = '' rss_header = """ @@ -542,12 +547,13 @@ def bb_rss(): """ return Response(rss_header + post_data + "\n\n", mimetype='text/xml') - except: - return str('

No data

') + except Exception as e: + #return str('

No data

') + return str(e) @app.route('/mailbox_rss') def mail_rss(): - mailbox_file = ast.literal_eval(os.popen('cat ../../gps_data_user_mailbox.txt').read()) + mailbox_file = ast.literal_eval(os.popen('cat ' + the_mailbox_file).read()) post_data = '' recipient = request.args.get('recipient').upper() rss_header = """ @@ -572,4 +578,64 @@ def mail_rss(): return Response(rss_header + post_data + "\n\n", mimetype='text/xml') if __name__ == '__main__': + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually gps_data.cfg)') + cli_args = arg_parser.parse_args() + parser = ConfigParser() + if not cli_args.CONFIG_FILE: + print('\n\nMust specify a config file with -c argument.\n\n') + parser.read(cli_args.CONFIG_FILE) + ###### Definitions ##### + # Title of the Dashboard + dashboard_title = parser.get('GPS_DATA', 'DASHBOARD_TITLE') + # Logo used on dashboard page + logo = parser.get('GPS_DATA', 'LOGO') + dash_port = int(parser.get('GPS_DATA', 'DASH_PORT')) + # IP to run server on + dash_host = parser.get('GPS_DATA', 'DASH_HOST') + #Description of dashboard to show on main page + description = parser.get('GPS_DATA', 'DESCRIPTION') + # The following will generate a help page for your users. + + # Data call type + if parser.get('GPS_DATA', 'CALL_TYPE') == 'unit': + data_call_type = 'Private Call' + if parser.get('GPS_DATA', 'CALL_TYPE') == 'group': + data_call_type = 'Group Call' + if parser.get('GPS_DATA', 'CALL_TYPE') == 'both': + data_call_type = 'Private or Group Call' + # DMR ID of GPS/Data application + data_call_id = parser.get('GPS_DATA', 'DATA_DMR_ID') + # Default APRS ssid + aprs_ssid = parser.get('GPS_DATA', 'USER_APRS_SSID') + + # Gateway contact info displayed on about page. + contact_name = parser.get('GPS_DATA', 'CONTACT_NAME') + contact_call = parser.get('GPS_DATA', 'CONTACT_CALL') + contact_email = parser.get('GPS_DATA', 'CONTACT_EMAIL') + contact_website = parser.get('GPS_DATA', 'CONTACT_WEBSITE') + + # Center dashboard map over these coordinates + map_center_lat = float(parser.get('GPS_DATA', 'MAP_CENTER_LAT')) + map_center_lon = float(parser.get('GPS_DATA', 'MAP_CENTER_LON')) + zoom_level = int(parser.get('GPS_DATA', 'ZOOM_LEVEL')) + map_theme = parser.get('GPS_DATA', 'MAP_THEME') + + # Time format for display + time_format = parser.get('GPS_DATA', 'TIME_FORMAT') + + # RSS feed link, shows in the link section of each RSS item. + rss_link = parser.get('GPS_DATA', 'RSS_LINK') + + # Default APRS comment for users. + default_comment = parser.get('GPS_DATA', 'USER_APRS_COMMENT') + + + # DO NOT MODIFY BELOW HERE. + bb_file = parser.get('GPS_DATA', 'BULLETIN_BOARD_FILE') + loc_file = parser.get('GPS_DATA', 'LOCATION_FILE') + emergency_sos_file = parser.get('GPS_DATA', 'EMERGENCY_SOS_FILE') + the_mailbox_file = parser.get('GPS_DATA', 'MAILBOX_FILE') + ######################## + app.run(debug = True, port=dash_port, host=dash_host)