From e6c61dd72aa9deaed11b888b1ff34f2c59a7e937 Mon Sep 17 00:00:00 2001 From: KF7EEL Date: Thu, 4 Mar 2021 12:42:33 -0800 Subject: [PATCH] rewrite dahboard config, update APRS receive --- bridge_gps_data-SAMPLE.cfg | 50 ++++++++++++++ bridge_gps_data.py | 114 ++++++++++++++++++-------------- config.py | 4 ++ gps_data-SAMPLE.cfg | 67 +++++++++++++++++-- gps_data.py | 101 ++++++++++++++++------------ scripts/aprs_receive/receive.py | 83 +++++++++++++++++++++++ scripts/dashboard/dashboard.py | 110 +++++++++++++++++++++++++----- 7 files changed, 413 insertions(+), 116 deletions(-) create mode 100644 scripts/aprs_receive/receive.py diff --git a/bridge_gps_data-SAMPLE.cfg b/bridge_gps_data-SAMPLE.cfg index a7a19ac..41959bf 100644 --- a/bridge_gps_data-SAMPLE.cfg +++ b/bridge_gps_data-SAMPLE.cfg @@ -148,6 +148,11 @@ IGATE_BEACON_ICON = /I IGATE_LATITUDE = 0000.00N IGATE_LONGITUDE = 00000.00W +# 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 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. EMAIL_SENDER: @@ -155,6 +160,51 @@ EMAIL_PASSWORD: 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/bridge_gps_data.py b/bridge_gps_data.py index 52bb768..d2e63a7 100644 --- a/bridge_gps_data.py +++ b/bridge_gps_data.py @@ -111,7 +111,7 @@ __email__ = 'n0mjs@me.com' # AT-D878 - Compressed UDP # MD-380 - Unified Data Transport hdr_type = '' -btf = '' +btf = -1 ssid = '' # From dmr_utils3, modified to decode entire packet. Works for 1/2 rate coded data. @@ -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: + dash_entries.insert(0, {'call': call, 'lat': lat, 'lon': lon, 'time':time, 'comment': comment}) + 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,36 +174,38 @@ 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.') #logger.info(dash_bb) + 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()) try: @@ -213,8 +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: + sos_info = {'call': sos_call, 'dmr_id': dmr_id, 'time': time, 'message':message} + with open(emergency_sos_file, 'w') as sos_file: sos_file.write(str(sos_info)) sos_file.close() logger.info('Saved SOS.') @@ -257,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 @@ -284,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: @@ -302,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) @@ -354,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) @@ -374,7 +381,7 @@ def process_sms(_rf_src, sms): elif 'A-' in sms and '@' in sms: #Example SMS text: @ARMDS A-This is a test. aprs_dest = re.sub('@| A-.*','',sms) - aprs_msg = re.sub('@.* A-|','',sms) + aprs_msg = re.sub('^@|.* A-|','',sms) logger.info('APRS message to ' + aprs_dest.upper() + '. Message: ' + aprs_msg) user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read()) if int_id(_rf_src) in user_settings and user_settings[int_id(_rf_src)][1]['ssid'] != '': @@ -1509,7 +1516,7 @@ class routerHBP(HBSYSTEM): # Fix for MD-380 by G7HIF aprs_lat = str(str(lat_deg) + str(lat_min).zfill(2) + '.' + str(lat_min_dec)[0:2]).zfill(7) + lat_dir aprs_lon = str(str(lon_deg) + str(lon_min).zfill(2) + '.' + str(lon_min_dec)[0:2]).zfill(8) + lon_dir - + # Form APRS packet #logger.info(aprs_loc_packet) logger.info('Lat: ' + str(aprs_lat) + ' Lon: ' + str(aprs_lon)) @@ -1535,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) @@ -1631,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: @@ -1765,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)') @@ -1829,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 4100364..eea47cf 100644 --- a/config.py +++ b/config.py @@ -162,6 +162,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 804875a..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. # @@ -116,7 +117,7 @@ STALE_DAYS: 1 # APRS_LOGIN_CALL, PASSCODE, SERVER, and PORT - Login settings for APRS-IS. [GPS_DATA] DATA_DMR_ID: 9099 -CALL_TYPE: both +CALL_TYPE: unit USER_APRS_SSID: 15 USER_APRS_COMMENT: HBLink3 D-APRS - @@ -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. -EMAIL_SENDER: -EMAIL_PASSWORD: +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 new file mode 100644 index 0000000..991d0da --- /dev/null +++ b/scripts/aprs_receive/receive.py @@ -0,0 +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 ' + 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() + print('User mail saved.') + +def aprs_filter(packet): + #if aprslib.parse(packet) in aprslib.parse(packet): + # print(aprslib.parse(packet)) + #else: + # pass + 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 c322b67..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 @@ -448,7 +452,7 @@ def mailbox(): if not recipient: mail_content = """

The Mailbox is a place where users can leave messages via DMR SMS. A user can leave a message for someone else by sending a specially formatted SMS to """ + data_call_id + """. - The message recipient can then use the mailbox to check for messages. Enter your call sign below to check for messages. See the help page for more information.

+ The message recipient can then use the mailbox to check for messages. You can also check for APRS mesages addressed to your DMR radio. Enter your call sign below to check for messages. See the help page for more information.

@@ -471,17 +475,30 @@ 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() + '''

''' for messages in mailbox_file: if messages['recipient'] == recipient.upper(): + sender = """ + + + + + """ if type(messages['time']) == str: loc_time = str(messages['time']) if type(messages['time']) == int or type(messages['time']) == float: loc_time = datetime.fromtimestamp(messages['time']).strftime(time_format) + if type(messages['dmr_id']) == str: + sender = """ + + + + + """ mail_content = mail_content + """
DMR ID:""" + str(messages['dmr_id']) + """
APRS Call:""" + str(messages['dmr_id']) + """
@@ -489,10 +506,7 @@ def mailbox(): - - - - + """ + sender + """ @@ -508,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 = """ @@ -532,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 = """ @@ -562,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)
From: """ + messages['call'] + """
DMR ID:""" + str(messages['dmr_id']) + """
Time: """ + loc_time + """