2020-12-29 23:38:03 -05:00
###############################################################################
# HBLink - Copyright (C) 2020 Cortney T. Buffington, N0MJS <n0mjs@me.com>
# GPS/Data - Copyright (C) 2020 Eric Craw, KF7EEL <kf7eel@qsl.net>
#
# 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
###############################################################################
'''
This is a web dashboard for the GPS / Data application .
'''
2021-02-28 16:49:22 -05:00
from flask import Flask , render_template , request , Response , Markup
2020-12-29 23:38:03 -05:00
import ast , os
2020-12-30 12:26:41 -05:00
from dashboard_settings import *
2021-02-26 23:22:24 -05:00
import folium
2021-02-27 09:57:44 -05:00
from folium . plugins import MarkerCluster
2021-02-26 23:48:24 -05:00
import re
2020-12-29 23:38:03 -05:00
app = Flask ( __name__ )
2020-12-30 12:26:41 -05:00
tbl_hdr = '''
2020-12-29 23:38:03 -05:00
< table style = " border-color: black; margin-left: auto; margin-right: auto; " border = " 2 " cellspacing = " 6 " cellpadding = " 2 " > < tbody >
'''
2020-12-30 12:26:41 -05:00
tbl_ftr = '''
2020-12-29 23:38:03 -05:00
< / tbody >
< / table >
'''
2020-12-30 12:26:41 -05:00
def get_loc_data ( ) :
try :
dash_loc = ast . literal_eval ( os . popen ( ' cat /tmp/gps_data_user_loc.txt ' ) . read ( ) )
tmp_loc = ' '
loc_hdr = '''
< tr >
< td style = " text-align: center; " >
< h2 > < strong > & nbsp ; Callsign & nbsp ; < / strong > < / h2 >
< / td >
< td style = " text-align: center; " >
< h2 > & nbsp ; < strong > Latitude < / strong > & nbsp ; < / h2 >
< / td >
< td style = " text-align: center; " >
< h2 > & nbsp ; < strong > Longitude < / strong > & nbsp ; < / h2 >
< / td >
< td style = " text-align: center; " >
< h2 > & nbsp ; < strong > Local Time < / strong > & nbsp ; < / h2 >
< / td >
< / tr >
'''
2021-02-27 02:19:09 -05:00
last_known_loc_list = [ ]
2021-02-27 00:53:27 -05:00
display_number = 15
2020-12-30 12:26:41 -05:00
for e in dash_loc :
2021-02-27 00:53:27 -05:00
if display_number == 0 :
break
else :
2021-02-27 02:19:09 -05:00
if e [ ' call ' ] in last_known_loc_list :
pass
if e [ ' call ' ] not in last_known_loc_list :
last_known_loc_list . append ( e [ ' call ' ] )
display_number = display_number - 1
tmp_loc = tmp_loc + ''' <tr>
2021-02-28 19:18:53 -05:00
< td style = " text-align: center; " > < a href = " view_map?track= ' ' ' + e[ ' call ' ] + ' ' ' " > < strong > ''' + e[ ' call ' ] + ''' < / strong > < / a > < / td >
2020-12-30 12:26:41 -05:00
< td style = " text-align: center; " > < strong > & nbsp ; ''' + str(e[ ' lat ' ]) + ''' & nbsp ; < / strong > < / td >
< td style = " text-align: center; " > < strong > & nbsp ; ''' + str(e[ ' lon ' ]) + ''' & nbsp ; < / strong > < / td >
< td style = " text-align: center; " > & nbsp ; ''' + e[ ' time ' ] + ''' & nbsp ; < / td >
< / tr > '''
2021-02-27 02:19:09 -05:00
return str ( str ( ' <h1 style= " text-align: center; " >Last Known Location</h1> ' ) + tbl_hdr + loc_hdr + tmp_loc + tbl_ftr )
2020-12-30 12:26:41 -05:00
except :
return str ( ' <h1 style= " text-align: center; " >No data</h1> ' )
def get_bb_data ( ) :
try :
dash_bb = ast . literal_eval ( os . popen ( ' cat /tmp/gps_data_user_bb.txt ' ) . read ( ) )
tmp_bb = ' '
bb_hdr = '''
< tr >
< td style = " text-align: center; " >
< h2 > < strong > & nbsp ; Callsign & nbsp ; < / strong > < / h2 >
< / td >
< td style = " text-align: center; " >
< h2 > & nbsp ; < strong > DMR ID < / strong > & nbsp ; < / h2 >
< / td >
< td style = " text-align: center; " >
< h2 > & nbsp ; < strong > Bulletin < / strong > & nbsp ; < / h2 >
< / td >
< td style = " text-align: center; " >
< h2 > & nbsp ; < strong > Local Time < / strong > & nbsp ; < / h2 >
< / td >
< / tr >
'''
for e in dash_bb :
tmp_bb = tmp_bb + ''' <tr>
< td style = " text-align: center; " > < strong > & nbsp ; ''' + e[ ' call ' ] + ''' & nbsp ; < / strong > < / td >
< td style = " text-align: center; " > ''' + str(e[ ' dmr_id ' ]) + ''' < / td >
2021-02-28 15:22:50 -05:00
< td style = " text-align: center; " > < strong > & nbsp ; ''' + e[ ' bulletin ' ] + ''' & nbsp ; < / strong > < / td >
2020-12-30 12:26:41 -05:00
< td style = " text-align: center; " > & nbsp ; ''' + e[ ' time ' ] + ''' & nbsp ; < / td >
< / tr > '''
return str ( ' <h1 style= " text-align: center; " >Bulletin Board</h1> ' + tbl_hdr + bb_hdr + tmp_bb + tbl_ftr )
except :
return str ( ' <h1 style= " text-align: center; " >No data</h1> ' )
2021-02-28 16:49:22 -05:00
def check_emergency ( ) :
# open emergency txt
try :
sos_file = ast . literal_eval ( os . popen ( ' cat /tmp/gps_data_user_sos.txt ' ) . read ( ) )
2021-02-28 19:59:53 -05:00
if ' @NOTICE ' in sos_file [ ' message ' ] and ' @SOS ' not in sos_file [ ' message ' ] :
notice_header = ' <span style= " background-color: #ffffff; color: #008000; " >NOTICE:</span> '
else :
notice_header = ' <span style= " background-color: #ff0000; color: #ffffff; " >EMERGENCY ACTIVATION</span> '
2021-02-28 16:49:22 -05:00
value = Markup ( """
2021-02-28 19:59:53 -05:00
< h1 style = " text-align: center; " > """ + notice_header + """ < / h1 >
2021-02-28 16:49:22 -05:00
< p > & nbsp ; < / p >
< table style = " width: 441px; margin-left: auto; margin-right: auto; " border = " 3 " >
< tbody >
< tr >
< td style = " width: 78.3667px; " > < span style = " text-decoration: underline; " > < strong > From : < / strong > < / span > < / td >
< td style = " width: 345.633px; text-align: center; " > < strong > """ + sos_file[ ' call ' ] + """ - """ + str(sos_file[ ' dmr_id ' ]) + """ < / strong > < / td >
< / tr >
< tr >
< td style = " width: 78.3667px; " > < span style = " text-decoration: underline; " > < strong > Message : < / strong > < / span > < / td >
< td style = " width: 345.633px; text-align: center; " > < strong > """ + sos_file[ ' message ' ] + """ < / strong > < / td >
< / tr >
< tr >
< td style = " width: 78.3667px; " > < span style = " text-decoration: underline; " > < strong > Time : < / strong > < / span > < / td >
< td style = " width: 345.633px; text-align: center; " > < strong > """ + sos_file[ ' time ' ] + """ < / strong > < / td >
< / tr >
< / tbody >
< / table >
< p > & nbsp ; < / p >
< button onclick = " window.open( ' view_map?track= " " " + sos_file [ ' call ' ] + """ &reload=30 ' , ' _blank ' ); " type= " button " class= " emergency_button " ><h1>View Station on Map</h1></button>
2021-02-28 17:31:40 -05:00
< p style = " text-align: center; " > < a href = " https://aprs.fi/ " " " + sos_file [ ' call ' ] + """ " ><strong>View on aprs.fi</strong></a></p>
2021-02-28 16:49:22 -05:00
< hr / >
""" )
return value
except :
return ' '
2021-02-26 23:48:24 -05:00
def aprs_to_latlon ( x ) :
degrees = int ( x ) / / 100
minutes = x - 100 * degrees
return degrees + minutes / 60
2020-12-29 23:38:03 -05:00
@app.route ( ' / ' )
2020-12-30 12:26:41 -05:00
def index ( ) :
2021-02-28 16:49:22 -05:00
value = Markup ( ' <strong>The HTML String</strong> ' )
2020-12-30 12:26:41 -05:00
#return get_data()
2021-02-28 16:49:22 -05:00
return render_template ( ' index.html ' , title = dashboard_title , logo = logo , emergency = check_emergency ( ) )
2020-12-30 12:26:41 -05:00
@app.route ( ' /bulletin_board ' )
def dash_bb ( ) :
return get_bb_data ( )
#return render_template('index.html', data = str(get_data()))
@app.route ( ' /positions ' )
def dash_loc ( ) :
return get_loc_data ( )
2021-02-28 16:49:22 -05:00
2021-01-02 18:55:18 -05:00
@app.route ( ' /help/ ' )
2021-01-02 18:45:35 -05:00
def help ( ) :
#return get_data()
2021-01-02 21:11:28 -05:00
return render_template ( ' help.html ' , title = dashboard_title , logo = logo , description = description , data_call_type = data_call_type , data_call_id = data_call_id , aprs_ssid = aprs_ssid )
2021-01-02 18:55:18 -05:00
@app.route ( ' /about/ ' )
2021-01-02 18:45:35 -05:00
def about ( ) :
#return get_data()
2021-01-02 18:48:59 -05:00
return render_template ( ' about.html ' , title = dashboard_title , logo = logo , contact_name = contact_name , contact_call = contact_call , contact_email = contact_email , contact_website = contact_website )
2021-02-27 17:56:58 -05:00
@app.route ( ' /view_map ' )
2021-02-27 00:05:18 -05:00
def view_map ( ) :
2021-02-28 13:52:34 -05:00
reload_time = request . args . get ( ' reload ' )
2021-02-27 17:56:58 -05:00
track_call = request . args . get ( ' track ' )
2021-02-26 23:22:24 -05:00
user_loc = ast . literal_eval ( os . popen ( ' cat /tmp/gps_data_user_loc.txt ' ) . read ( ) )
2021-02-27 02:19:09 -05:00
last_known_list = [ ]
2021-02-28 16:49:22 -05:00
try :
if track_call :
#folium_map = folium.Map(location=map_center, 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 ' ] ) ) )
user_lon = aprs_to_latlon ( float ( re . sub ( ' [A-Za-z] ' , ' ' , user_coord [ ' lon ' ] ) ) )
if ' S ' in user_coord [ ' lat ' ] :
user_lat = - user_lat
if ' W ' in user_coord [ ' lon ' ] :
user_lon = - user_lon
if user_coord [ ' call ' ] not in last_known_list and user_coord [ ' call ' ] == track_call :
folium_map = folium . Map ( location = [ user_lat , user_lon ] , tiles = " Stamen Toner " , zoom_start = 15 )
marker_cluster = MarkerCluster ( ) . add_to ( folium_map )
folium . Marker ( [ user_lat , user_lon ] , popup = """ <i>
< table style = " width: 150px; " >
< tbody >
< tr >
< td style = " text-align: center; " > Last Location : < / td >
< / tr >
< tr >
< td style = " text-align: center; " > < strong > """ + str(user_coord[ ' call ' ]) + """ < / strong > < / td >
< / tr >
< tr >
< td style = " text-align: center; " > < em > """ + user_coord[ ' time ' ] + """ < / em > < / td >
< / tr >
< / tbody >
< / table >
< / i >
""" , icon=folium.Icon(color= " red " , icon= " record " ), tooltip=str(user_coord[ ' call ' ])).add_to(folium_map)
last_known_list . append ( user_coord [ ' call ' ] )
if user_coord [ ' call ' ] in last_known_list and user_coord [ ' call ' ] == track_call :
folium . CircleMarker ( [ user_lat , user_lon ] , popup = """
< table style = " width: 150px; " >
< tbody >
< tr >
< td style = " text-align: center; " > < strong > """ + user_coord[ ' call ' ] + """ < / strong > < / td >
< / tr >
< tr >
< td style = " text-align: center; " > < em > """ + user_coord[ ' time ' ] + """ < / em > < / td >
< / tr >
< / tbody >
< / table >
""" , tooltip=str(user_coord[ ' call ' ]), fill=True, fill_color= " #3186cc " , radius=4).add_to(marker_cluster)
#return folium_map._repr_html_()
if not reload_time :
reload_time = 120
return ' {} {} ' . format ( ''' <head>
< meta charset = " UTF-8 " >
< meta http - equiv = " refresh " content = " ' ' ' + str(reload_time) + " """ >
< title > """ + dashboard_title + """ - Tracking """ + track_call + """ < / title > < / head > < p style = " text-align: center; " > < strong > """ + dashboard_title + """ - Tracking """ + track_call + """ < / strong > < / p >
< p style = " text-align: center; " > < em > Page automatically reloads every """ + str(reload_time) + """ seconds . < / em > < / p >
< p style = " text-align: center; " >
< select name = " sample " onchange = " location = this.value; " >
< option value = " view_map?track= " " " + track_call + """ &reload=120 " >2 Minutes</option>
< option value = " view_map?track= " " " + track_call + """ &reload= " >Don ' t Reload</option>
< option value = " view_map?track= " " " + track_call + """ &reload=30 " >30 Seconds</option>
< option value = " view_map?track= " " " + track_call + """ &reload=5 " >5 Minutes</option>
< option value = " view_map?track= " " " + track_call + """ &reload=600 " >10 Minutes</option>
< / select >
2021-02-28 19:18:53 -05:00
< p style = " text-align: center; " > < button onclick = " self.close() " > Close < / button > < button onclick = " history.back() " > Back < / button >
2021-02-28 16:49:22 -05:00
< / p > """ , folium_map._repr_html_())
except :
return """ <h1 style= " text-align: center; " >Station not found.</h1>
2021-02-28 19:18:53 -05:00
< p style = " text-align: center; " > < button onclick = " self.close() " > Close Window < / button >
< / p > """
2021-02-27 17:56:58 -05:00
if not track_call :
2021-02-28 13:52:34 -05:00
folium_map = folium . Map ( location = map_center , tiles = " Stamen Toner " , zoom_start = int ( zoom_level ) )
2021-02-27 17:56:58 -05:00
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 ' ] ) ) )
user_lon = aprs_to_latlon ( float ( re . sub ( ' [A-Za-z] ' , ' ' , user_coord [ ' lon ' ] ) ) )
if ' S ' in user_coord [ ' lat ' ] :
user_lat = - user_lat
if ' W ' in user_coord [ ' lon ' ] :
user_lon = - user_lon
if user_coord [ ' call ' ] not in last_known_list :
2021-02-28 13:52:34 -05:00
folium . Marker ( [ user_lat , user_lon ] , popup = """ <i>
< table style = " width: 150px; " >
< tbody >
< tr >
< td style = " text-align: center; " > Last Location : < / td >
< / tr >
< tr >
< td style = " text-align: center; " > < strong > """ + user_coord[ ' call ' ] + """ < / strong > < / td >
< / tr >
< tr >
< td style = " text-align: center; " > < em > """ + user_coord[ ' time ' ] + """ < / em > < / td >
< / tr >
< tr >
< td style = " text-align: center; " > < strong > < A href = " view_map?track= " " " + user_coord [ ' call ' ] + """ " target= " _blank " >Track Station</A></strong></td>
< / tr >
< / tbody >
< / table >
< / i > """ , icon=folium.Icon(color= " red " , icon= " record " ), tooltip=str(user_coord[ ' call ' ])).add_to(folium_map)
2021-02-27 17:56:58 -05:00
last_known_list . append ( user_coord [ ' call ' ] )
if user_coord [ ' call ' ] in last_known_list :
2021-02-28 13:52:34 -05:00
folium . CircleMarker ( [ user_lat , user_lon ] , popup = """
< table style = " width: 150px; " >
< tbody >
< tr >
< td style = " text-align: center; " > < strong > """ + user_coord[ ' call ' ] + """ < / strong > < / td >
< / tr >
< tr >
< td style = " text-align: center; " > < em > """ + user_coord[ ' time ' ] + """ < / em > < / td >
< / tr >
< / tbody >
< / table >
""" , tooltip=str(user_coord[ ' call ' ]), fill=True, fill_color= " #3186cc " , radius=4).add_to(marker_cluster)
2021-02-27 17:56:58 -05:00
return folium_map . _repr_html_ ( )
2021-02-27 00:05:18 -05:00
@app.route ( ' /map/ ' )
def map ( ) :
return render_template ( ' map.html ' , title = dashboard_title , logo = logo )
2021-01-02 18:48:59 -05:00
2021-02-28 15:22:50 -05:00
@app.route ( ' /bulletin_rss.xml ' )
def bb_rss ( ) :
2021-02-28 17:31:40 -05:00
try :
dash_bb = ast . literal_eval ( os . popen ( ' cat /tmp/gps_data_user_bb.txt ' ) . read ( ) )
post_data = ' '
rss_header = """ <?xml version= " 1.0 " encoding= " UTF-8 " ?>
< rss version = " 2.0 " >
< channel >
< title > """ + dashboard_title + """ - Bulletin Board Feed < / title >
< link > """ + rss_link + """ < / link >
< description > This is the Bulletin Board feed from """ + dashboard_title + """ < / description > """
for entry in dash_bb :
post_data = post_data + """
< item >
< title > """ + entry[ ' call ' ] + ' - ' + str(entry[ ' dmr_id ' ]) + """ < / title >
< link > """ + rss_link + """ < / link >
< description > """ + entry[ ' bulletin ' ] + """ - """ + entry[ ' time ' ] + """ < / description >
< / item >
"""
return Response ( rss_header + post_data + " \n </channel> \n </rss> " , mimetype = ' text/xml ' )
except :
return str ( ' <h1 style= " text-align: center; " >No data</h1> ' )
2021-01-02 18:48:59 -05:00
if __name__ == ' __main__ ' :
2020-12-30 19:40:39 -05:00
app . run ( debug = True , port = dash_port , host = dash_host )