Compare commits
56 Commits
Author | SHA1 | Date |
---|---|---|
KF7EEL | be1c306af8 | |
KF7EEL | 421a0ddc49 | |
KF7EEL | e97e5cff32 | |
KF7EEL | 7f46a19749 | |
KF7EEL | e6ace9e72d | |
KF7EEL | 1fb22d01c6 | |
KF7EEL | 4e0c87dac9 | |
KF7EEL | ef850596d1 | |
KF7EEL | 247168d567 | |
KF7EEL | 6a6fdf367a | |
KF7EEL | b860dc75ba | |
KF7EEL | 6cc6f7fc81 | |
KF7EEL | 873bbb6ce7 | |
KF7EEL | 2e5dec5cac | |
KF7EEL | 239c0730c0 | |
KF7EEL | fcff610fd3 | |
KF7EEL | 6a1476af51 | |
KF7EEL | 295dfbda1f | |
KF7EEL | e61f90fcfd | |
KF7EEL | 599f549ab9 | |
KF7EEL | 65ab256b7f | |
KF7EEL | 104cab1c29 | |
KF7EEL | eded346f91 | |
KF7EEL | a91eb65273 | |
KF7EEL | 5b1e250d57 | |
KF7EEL | d332604f26 | |
KF7EEL | 888d8c19b7 | |
KF7EEL | cb47556205 | |
KF7EEL | f8cdf8fead | |
KF7EEL | d0e90531c6 | |
KF7EEL | dccf561e3d | |
KF7EEL | 751af20357 | |
KF7EEL | 12dc612c2e | |
KF7EEL | dfedf9eade | |
KF7EEL | ee7c7eccf1 | |
KF7EEL | f4010939dc | |
KF7EEL | 9b19a09eac | |
KF7EEL | 89176e922c | |
KF7EEL | 7febe66a76 | |
KF7EEL | ce36850621 | |
KF7EEL | 3368683a1f | |
KF7EEL | d84e937c1f | |
KF7EEL | ec82ce8b24 | |
KF7EEL | 40a3030694 | |
KF7EEL | f68c5d0691 | |
KF7EEL | bf9b45d44f | |
KF7EEL | fb9226e04f | |
KF7EEL | b00ba9ea62 | |
KF7EEL | 9b14982abe | |
KF7EEL | 61fd13514b | |
KF7EEL | 77309d5ca6 | |
KF7EEL | db77a165b4 | |
KF7EEL | 83274856a3 | |
KF7EEL | 72ed9f2d2b | |
KF7EEL | 1b904e9af7 | |
KF7EEL | db068d859e |
14
README.md
14
README.md
|
@ -1,3 +1,5 @@
|
|||
### This project has morphed into HBNet. It is still under heavy development and not ready for production. You can see it [here](https://github.com/kf7eel/hblink3/tree/hbnet). The example/development server can be found at [https://demo.hbnet.xyz](https://demo.hbnet.xyz).
|
||||
|
||||
# GPS/Data Application
|
||||
|
||||
This repository contains everything needed to decode DMR GPS packets and SMS for HBLink3. This application can act as a master or peer and receive data as a group call or private call. It is designed to work in a multi system/user network.
|
||||
|
@ -15,8 +17,10 @@ Files modified from original master branch of HBLink3:
|
|||
|
||||
#### Optional Modules
|
||||
* Flask - Required for dashboard
|
||||
* smtplib - Required for sending email. If pip fails to install module, it may already be installed as most Linux distrobutions have this module by default.
|
||||
* smtplib - Required for sending email. If pip fails to install module, it may already be installed as most Linux distributions have this module by default.
|
||||
* traceback - If pip fails to install module, it may already be installed as most Linux distrobutions have this module by default.
|
||||
* slixmpp - Required for upcoming XMPP gateway.
|
||||
* folium - Required for mapping on dashboard.
|
||||
|
||||
This should work for DMR radios that send location data as a UTF-8 NMEA sentence. I am hopping to add support for more radios in the future.
|
||||
|
||||
|
@ -115,6 +119,14 @@ The web dashboard is completely optional. Python module flask is required for th
|
|||
|
||||
**At this time, only sending of messages from DMR SMS to APRS-IS is supported.** I find this feature very pointless because it will only go one way, but someone else may find it important. **Messages from sent from APRS-IS to DMR SMS will not work.** I have not written the code for this yet. It will likley be a long time before this is a possibility.
|
||||
|
||||
## APRS TOCALL
|
||||
|
||||
The project was granted a [tocall](http://www.aprs.org/aprs11/tocalls.txt) of **APHBLx** by Bob Bruniga, WB4APR. This will identify that your APRS position came from HBLink. The x on the end can be any letter/number. Here are the current designations of APHBLx:
|
||||
|
||||
* **APHBL3** - HBlink3 D-APRS gateway
|
||||
* **APHBLD** - DMRlink D-APRS gateway (the IPSC version of the project)
|
||||
* **_APHBLS_** - Planned, but not in use: HBLink3 via KISS TNC
|
||||
|
||||
## Configuration
|
||||
|
||||
See hblink_SAMPLE.cfg, rules_SAMPLE.py, and gps_data_SAMPLE.cfg for examples.
|
||||
|
|
|
@ -46,6 +46,19 @@ SUB_ACL: DENY:1
|
|||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
# APRS - BY IU7IGU
|
||||
# Enabling "APRS" will configure APRS-Beaconing of Master's connection
|
||||
# like repeater and hotspots.
|
||||
# REPORT_INTERVAL in Minute (ALLOW only > 3 Minutes)
|
||||
# CALLSIGN: Callsign that will pubblish data on aprs server
|
||||
# MESSAGE: This message will print on APRS description together RX and TX Frequency
|
||||
|
||||
[APRS]
|
||||
ENABLED: False
|
||||
REPORT_INTERVAL: 15
|
||||
CALLSIGN:HB1LNK-11
|
||||
SERVER:euro.aprs2.net
|
||||
MESSAGE:Connesso ad HBLINK
|
||||
|
||||
# NOT YET WORKING: NETWORK REPORTING CONFIGURATION
|
||||
# Enabling "REPORT" will configure a socket-based reporting
|
||||
|
@ -135,6 +148,14 @@ IGATE_BEACON_ICON = /I
|
|||
IGATE_LATITUDE = 0000.00N
|
||||
IGATE_LONGITUDE = 00000.00W
|
||||
|
||||
# User settings file, MUST configure using absolute path.
|
||||
USER_SETTINGS_FILE: /path/to/user_settings.txt
|
||||
|
||||
# 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:
|
||||
|
@ -142,6 +163,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
|
||||
|
@ -191,6 +257,9 @@ TGID_ACL: PERMIT:ALL
|
|||
[MASTER-1]
|
||||
MODE: MASTER
|
||||
ENABLED: True
|
||||
# The APRS setting below has to do with the beaconing of PEER conections, and not GPS positions
|
||||
# from radios. This setting will not affect gps_data.py
|
||||
APRS: False
|
||||
REPEAT: True
|
||||
MAX_PEERS: 10
|
||||
EXPORT_AMBE: False
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#
|
||||
###############################################################################
|
||||
# Copyright (C) 2016-2019 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
# GPS/Data - Copyright (C) 2020 Eric Craw, KF7EEL <kf7eel@qsl.net>
|
||||
# Annotated modifications Copyright (C) 2021 Xavier FRS2013
|
||||
#
|
||||
# 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
|
||||
|
@ -55,6 +57,7 @@ import pickle
|
|||
# The module needs logging, but handlers, etc. are controlled by the parent
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
import traceback
|
||||
|
||||
# Import UNIT time from rules.py
|
||||
from rules import UNIT_TIME, STATIC_UNIT
|
||||
|
@ -108,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.
|
||||
|
@ -157,30 +160,80 @@ def aprs_send(packet):
|
|||
AIS.close()
|
||||
logger.info('Packet sent to APRS-IS.')
|
||||
|
||||
def dashboard_loc_write(call, lat, lon, time):
|
||||
#try:
|
||||
def dashboard_loc_write(call, lat, lon, time, comment):
|
||||
dash_entries = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read())
|
||||
# except:
|
||||
# dash_entries = []
|
||||
dash_entries.insert(0, {'call': call, 'lat': lat, 'lon': lon, 'time':time})
|
||||
with open("/tmp/gps_data_user_loc.txt", 'w') as user_loc_file:
|
||||
user_loc_file.write(str(dash_entries[:15]))
|
||||
dash_entries.insert(0, {'call': call, 'lat': lat, 'lon': lon, 'time':time, 'comment':comment})
|
||||
# Clear old entries
|
||||
list_index = 0
|
||||
call_count = 0
|
||||
new_dash_entries = []
|
||||
for i in dash_entries:
|
||||
if i['call'] == call:
|
||||
if call_count >= 25:
|
||||
pass
|
||||
else:
|
||||
new_dash_entries.append(i)
|
||||
call_count = call_count + 1
|
||||
|
||||
if call != i['call']:
|
||||
new_dash_entries.append(i)
|
||||
pass
|
||||
list_index = list_index + 1
|
||||
with open(loc_file, 'w') as user_loc_file:
|
||||
user_loc_file.write(str(new_dash_entries[:500]))
|
||||
user_loc_file.close()
|
||||
logger.info('User location saved for dashboard')
|
||||
#logger.info(dash_entries)
|
||||
|
||||
|
||||
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, 'bulliten':bulletin})
|
||||
with open("/tmp/gps_data_user_bb.txt", 'w') as user_bb_file:
|
||||
user_bb_file.write(str(dash_bb[:10]))
|
||||
dash_bb.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'bulletin':bulletin})
|
||||
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 ' + the_mailbox_file).read())
|
||||
mail_file.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'message':message, 'recipient': recipient})
|
||||
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 ' + 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(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_file).read())
|
||||
try:
|
||||
if user_settings[dmr_id][1]['ssid'] == '':
|
||||
sos_call = user_settings[dmr_id][0]['call'] + '-' + user_ssid
|
||||
else:
|
||||
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(emergency_sos_file, 'w') as sos_file:
|
||||
sos_file.write(str(sos_info))
|
||||
sos_file.close()
|
||||
logger.info('Saved SOS.')
|
||||
|
||||
# Send email via SMTP function
|
||||
def send_email(to_email, email_subject, email_message):
|
||||
global smtp_server
|
||||
|
@ -205,34 +258,39 @@ def decdeg2dms(dd):
|
|||
def user_setting_write(dmr_id, setting, value):
|
||||
## try:
|
||||
# Open file and load as dict for modification
|
||||
with open("./user_settings.txt", 'r') as f:
|
||||
with open(user_settings_file, 'r') as f:
|
||||
## if f.read() == '{}':
|
||||
## user_dict = {}
|
||||
user_dict = ast.literal_eval(f.read())
|
||||
logger.info('Current settings: ' + str(user_dict))
|
||||
if dmr_id not in user_dict:
|
||||
user_dict[dmr_id] = [{'call': str(get_alias((dmr_id), subscriber_ids))}, {'ssid': ''}, {'icon': ''}, {'comment': ''}]
|
||||
|
||||
if setting.upper() == 'ICON':
|
||||
user_dict[dmr_id][2]['icon'] = value
|
||||
if setting.upper() == 'SSID':
|
||||
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': ''}]
|
||||
if setting.upper() == 'PIN':
|
||||
try:
|
||||
if user_dict[dmr_id]:
|
||||
user_dict[dmr_id][4]['pin'] = value
|
||||
if not user_dict[dmr_id]:
|
||||
user_dict[dmr_id] = [{'call': str(get_alias((dmr_id), subscriber_ids))}, {'ssid': ''}, {'icon': ''}, {'comment': ''}, {'pin': pin}]
|
||||
except:
|
||||
user_dict[dmr_id].append({'pin': value})
|
||||
f.close()
|
||||
logger.info('Loaded user settings. Preparing to write...')
|
||||
# Write modified dict to file
|
||||
with open("./user_settings.txt", 'w') as user_dict_file:
|
||||
with open(user_settings_file, 'w') as user_dict_file:
|
||||
user_dict_file.write(str(user_dict))
|
||||
user_dict_file.close()
|
||||
logger.info('User setting saved')
|
||||
f.close()
|
||||
packet_assembly = ''
|
||||
## except:
|
||||
## logger.info('No data file found, creating one.')
|
||||
## #Path('./user_settings.txt').mkdir(parents=True, exist_ok=True)
|
||||
## Path('./user_settings.txt').touch()
|
||||
|
||||
|
||||
# Process SMS, do something bases on message
|
||||
|
||||
def process_sms(_rf_src, sms):
|
||||
|
@ -246,19 +304,37 @@ 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))
|
||||
elif '@PIN' in sms:
|
||||
user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), int(re.sub('@PIN |@PIN','',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), strftime('%H:%M:%S - %m/%d/%y'), re.sub('@BB|@BB ','',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:
|
||||
email_message = re.sub('.*@|.* E-', '', sms)
|
||||
to_email = re.sub(' E-.*', '', sms)
|
||||
email_message = str(re.sub('.*@|.* E-', '', sms))
|
||||
to_email = str(re.sub(' E-.*', '', sms))
|
||||
email_subject = 'New message from ' + str(get_alias(int_id(_rf_src), subscriber_ids))
|
||||
logger.info('Email to: ' + to_email)
|
||||
logger.info('Message: ' + email_message)
|
||||
try:
|
||||
send_email(to_email, email_subject, email_message)
|
||||
logger.info('Email sent.')
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Failed to send email.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
elif '@SOS' in sms or '@NOTICE' in sms:
|
||||
sos_write(int_id(_rf_src), time(), sms)
|
||||
elif '@REM SOS' == sms:
|
||||
os.remove(emergency_sos_file)
|
||||
logger.info('Removing SOS')
|
||||
elif '@' and 'M-' in sms:
|
||||
message = re.sub('^@|.* M-|','',sms)
|
||||
recipient = re.sub('@| M-.*','',sms)
|
||||
mailbox_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time(), message, str(recipient).upper())
|
||||
elif '@REM MAIL' == sms:
|
||||
mailbox_delete(_rf_src)
|
||||
elif '@MH' in sms:
|
||||
grid_square = re.sub('@MH ', '', sms)
|
||||
if len(grid_square) < 6:
|
||||
|
@ -282,7 +358,7 @@ def process_sms(_rf_src, sms):
|
|||
logger.info('Latitude: ' + str(aprs_lat))
|
||||
logger.info('Longitude: ' + str(aprs_lon))
|
||||
# 14FRS2013 simplified and moved settings retrieval
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
|
@ -303,7 +379,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)
|
||||
|
@ -311,19 +387,21 @@ def process_sms(_rf_src, sms):
|
|||
try:
|
||||
aprslib.parse(aprs_loc_packet)
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, strftime('%H:%M:%S - %m/%d/%y'))
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, time(), comment)
|
||||
#logger.info('Sent manual position to APRS')
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Exception. Not uploaded')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
packet_assembly = ''
|
||||
|
||||
|
||||
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())
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
if int_id(_rf_src) in user_settings and user_settings[int_id(_rf_src)][1]['ssid'] != '':
|
||||
ssid = user_settings[int_id(_rf_src)][1]['ssid']
|
||||
else:
|
||||
|
@ -334,15 +412,19 @@ def process_sms(_rf_src, sms):
|
|||
aprslib.parse(aprs_msg_pkt)
|
||||
aprs_send(aprs_msg_pkt)
|
||||
#logger.info('Packet sent.')
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Error uploading MSG packet.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
try:
|
||||
if sms in cmd_list:
|
||||
logger.info('Executing command/script.')
|
||||
os.popen(cmd_list[sms]).read()
|
||||
packet_assembly = ''
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Exception. Command possibly not in list, or other error.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
packet_assembly = ''
|
||||
else:
|
||||
pass
|
||||
|
@ -1446,13 +1528,18 @@ class routerHBP(HBSYSTEM):
|
|||
lon_min = ba2num(bptc_decode(_data)[46:52])
|
||||
lat_min_dec = str(ba2num(bptc_decode(_data)[24:38])).zfill(4)
|
||||
lon_min_dec = str(ba2num(bptc_decode(_data)[52:66])).zfill(4)
|
||||
aprs_lat = str(str(lat_deg) + str(lat_min) + '.' + str(lat_min_dec)[0:2]).zfill(7) + lat_dir
|
||||
aprs_lon = str(str(lon_deg) + str(lon_min) + '.' + str(lon_min_dec)[0:2]).zfill(8) + lon_dir
|
||||
# Old MD-380 coordinate format, keep here until new is confirmed working.
|
||||
#aprs_lat = str(str(lat_deg) + str(lat_min) + '.' + str(lat_min_dec)[0:2]).zfill(7) + lat_dir
|
||||
#aprs_lon = str(str(lon_deg) + str(lon_min) + '.' + str(lon_min_dec)[0:2]).zfill(8) + lon_dir
|
||||
# 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))
|
||||
# 14FRS2013 simplified and moved settings retrieval
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
|
@ -1473,7 +1560,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)
|
||||
|
@ -1484,10 +1571,12 @@ class routerHBP(HBSYSTEM):
|
|||
float(lat_deg) < 91
|
||||
float(lon_deg) < 121
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, strftime('%H:%M:%S - %m/%d/%y'))
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, time(), comment)
|
||||
#logger.info('Sent APRS packet')
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Error. Failed to send packet. Packet may be malformed.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
udt_block = 1
|
||||
hdr_type = ''
|
||||
else:
|
||||
|
@ -1546,7 +1635,7 @@ class routerHBP(HBSYSTEM):
|
|||
# Begin APRS format and upload
|
||||
# Disable opening file for reading to reduce "collision" or reading and writing at same time.
|
||||
# 14FRS2013 simplified and moved settings retrieval
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
|
@ -1567,14 +1656,16 @@ 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:
|
||||
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:
|
||||
# Try parse of APRS packet. If it fails, it will not upload to APRS-IS
|
||||
aprslib.parse(aprs_loc_packet)
|
||||
|
@ -1582,9 +1673,11 @@ class routerHBP(HBSYSTEM):
|
|||
float(loc.lat)
|
||||
float(loc.lon)
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, str(loc.lat[0:7]) + str(loc.lat_dir), str(loc.lon[0:8]) + str(loc.lon_dir), strftime('%H:%M:%S - %m/%d/%y'))
|
||||
except:
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, str(loc.lat[0:7]) + str(loc.lat_dir), str(loc.lon[0:8]) + str(loc.lon_dir), time(), comment)
|
||||
except Exception as error_exception:
|
||||
logger.info('Failed to parse packet. Packet may be deformed. Not uploaded.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
#final_packet = ''
|
||||
# Get callsign based on DMR ID
|
||||
# End APRS-IS upload
|
||||
|
@ -1697,32 +1790,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()
|
||||
|
||||
|
||||
|
||||
# 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)')
|
||||
|
@ -1755,6 +1822,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_file).is_file():
|
||||
pass
|
||||
else:
|
||||
Path(user_settings_file).touch()
|
||||
with open(user_settings_file, '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'
|
||||
|
@ -1812,6 +1916,7 @@ if __name__ == '__main__':
|
|||
systems[system] = routerHBP(system, CONFIG, report_server)
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP'])
|
||||
logger.debug('(GLOBAL) %s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system])
|
||||
#aprs_upload(CONFIG)
|
||||
|
||||
def loopingErrHandle(failure):
|
||||
logger.error('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error in timed loop.\n %s', failure)
|
||||
|
|
|
@ -161,6 +161,11 @@ 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'),
|
||||
'USER_SETTINGS_FILE': config.get(section, 'USER_SETTINGS_FILE'),
|
||||
|
||||
})
|
||||
if not CONFIG['LOGGER']['LOG_FILE']:
|
||||
|
|
|
@ -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,62 @@ IGATE_BEACON_ICON = /I
|
|||
IGATE_LATITUDE = 0000.00N
|
||||
IGATE_LONGITUDE = 00000.00W
|
||||
|
||||
# User settings file, MUST configure using absolute path.
|
||||
USER_SETTINGS_FILE: /path/to/user_settings.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
|
||||
|
|
261
gps_data.py
261
gps_data.py
|
@ -48,6 +48,7 @@ import const
|
|||
# The module needs logging logging, but handlers, etc. are controlled by the parent
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
import traceback
|
||||
|
||||
# Other modules we need for data and GPS
|
||||
from bitarray import bitarray
|
||||
|
@ -70,12 +71,12 @@ from gps_functions import cmd_list
|
|||
# Module for maidenhead grids
|
||||
try:
|
||||
import maidenhead as mh
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Error importing maidenhead module, make sure it is installed.')
|
||||
# Module for sending email
|
||||
try:
|
||||
import smtplib
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Error importing smtplib module, make sure it is installed.')
|
||||
|
||||
#Modules for APRS settings
|
||||
|
@ -92,6 +93,8 @@ __maintainer__ = 'Eric Craw, KF7EEL'
|
|||
__email__ = 'kf7eel@qsl.net'
|
||||
__status__ = 'pre-alpha'
|
||||
|
||||
# Known to work with: AT-D878
|
||||
|
||||
# Must have the following at line 1054 in bridge.py to forward group vcsbk, also there is a typo there:
|
||||
# self.group_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _frame_type, _dtype_vseq, _stream_id, _data)
|
||||
|
||||
|
@ -101,7 +104,7 @@ __status__ = 'pre-alpha'
|
|||
# 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.
|
||||
|
@ -149,31 +152,88 @@ def aprs_send(packet):
|
|||
AIS.sendall(packet)
|
||||
AIS.close()
|
||||
logger.info('Packet sent to APRS-IS.')
|
||||
# For future use
|
||||
##def position_timer(aprs_call):
|
||||
## dash_entries = ast.literal_eval(os.popen('cat ' + loc_file).read())
|
||||
## for i in dash_entries:
|
||||
## if aprs_call == i['call']:
|
||||
## if time.time()
|
||||
|
||||
def dashboard_loc_write(call, lat, lon, time):
|
||||
#try:
|
||||
|
||||
def dashboard_loc_write(call, lat, lon, time, comment):
|
||||
dash_entries = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read())
|
||||
# except:
|
||||
# dash_entries = []
|
||||
dash_entries.insert(0, {'call': call, 'lat': lat, 'lon': lon, 'time':time})
|
||||
with open("/tmp/gps_data_user_loc.txt", 'w') as user_loc_file:
|
||||
user_loc_file.write(str(dash_entries[:15]))
|
||||
dash_entries.insert(0, {'call': call, 'lat': lat, 'lon': lon, 'time':time, 'comment':comment})
|
||||
# Clear old entries
|
||||
list_index = 0
|
||||
call_count = 0
|
||||
new_dash_entries = []
|
||||
for i in dash_entries:
|
||||
if i['call'] == call:
|
||||
if call_count >= 25:
|
||||
pass
|
||||
else:
|
||||
new_dash_entries.append(i)
|
||||
call_count = call_count + 1
|
||||
|
||||
if call != i['call']:
|
||||
new_dash_entries.append(i)
|
||||
pass
|
||||
list_index = list_index + 1
|
||||
with open(loc_file, 'w') as user_loc_file:
|
||||
user_loc_file.write(str(new_dash_entries[:500]))
|
||||
user_loc_file.close()
|
||||
logger.info('User location saved for dashboard')
|
||||
#logger.info(dash_entries)
|
||||
|
||||
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, 'bulliten':bulletin})
|
||||
with open("/tmp/gps_data_user_bb.txt", 'w') as user_bb_file:
|
||||
user_bb_file.write(str(dash_bb[:10]))
|
||||
dash_bb.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'bulletin':bulletin})
|
||||
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 ' + the_mailbox_file).read())
|
||||
mail_file.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'message':message, 'recipient': recipient})
|
||||
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 ' + 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(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_file).read())
|
||||
try:
|
||||
if user_settings[dmr_id][1]['ssid'] == '':
|
||||
sos_call = user_settings[dmr_id][0]['call'] + '-' + user_ssid
|
||||
else:
|
||||
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(emergency_sos_file, 'w') as sos_file:
|
||||
sos_file.write(str(sos_info))
|
||||
sos_file.close()
|
||||
logger.info('Saved SOS.')
|
||||
|
||||
# Send email via SMTP function
|
||||
def send_email(to_email, email_subject, email_message):
|
||||
global smtp_server
|
||||
|
@ -198,39 +258,44 @@ def decdeg2dms(dd):
|
|||
def user_setting_write(dmr_id, setting, value):
|
||||
## try:
|
||||
# Open file and load as dict for modification
|
||||
with open("./user_settings.txt", 'r') as f:
|
||||
with open(user_settings_file, 'r') as f:
|
||||
## if f.read() == '{}':
|
||||
## user_dict = {}
|
||||
user_dict = ast.literal_eval(f.read())
|
||||
logger.info('Current settings: ' + str(user_dict))
|
||||
if dmr_id not in user_dict:
|
||||
user_dict[dmr_id] = [{'call': str(get_alias((dmr_id), subscriber_ids))}, {'ssid': ''}, {'icon': ''}, {'comment': ''}]
|
||||
|
||||
if setting.upper() == 'ICON':
|
||||
user_dict[dmr_id][2]['icon'] = value
|
||||
if setting.upper() == 'SSID':
|
||||
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': ''}]
|
||||
if setting.upper() == 'PIN':
|
||||
try:
|
||||
if user_dict[dmr_id]:
|
||||
user_dict[dmr_id][4]['pin'] = value
|
||||
if not user_dict[dmr_id]:
|
||||
user_dict[dmr_id] = [{'call': str(get_alias((dmr_id), subscriber_ids))}, {'ssid': ''}, {'icon': ''}, {'comment': ''}, {'pin': pin}]
|
||||
except:
|
||||
user_dict[dmr_id].append({'pin': value})
|
||||
f.close()
|
||||
logger.info('Loaded user settings. Preparing to write...')
|
||||
# Write modified dict to file
|
||||
with open("./user_settings.txt", 'w') as user_dict_file:
|
||||
with open(user_settings_file, 'w') as user_dict_file:
|
||||
user_dict_file.write(str(user_dict))
|
||||
user_dict_file.close()
|
||||
logger.info('User setting saved')
|
||||
f.close()
|
||||
packet_assembly = ''
|
||||
## except:
|
||||
## logger.info('No data file found, creating one.')
|
||||
## #Path('./user_settings.txt').mkdir(parents=True, exist_ok=True)
|
||||
## Path('./user_settings.txt').touch()
|
||||
|
||||
|
||||
# Process SMS, do something bases on message
|
||||
|
||||
def process_sms(_rf_src, sms):
|
||||
if sms == 'ID':
|
||||
logger.info(str(get_alias(int_id(from_id), subscriber_ids)) + ' - ' + str(int_id(from_id)))
|
||||
logger.info(str(get_alias(int_id(_rf_src), subscriber_ids)) + ' - ' + str(int_id(_rf_src)))
|
||||
elif sms == 'TEST':
|
||||
logger.info('It works!')
|
||||
elif '@ICON' in sms:
|
||||
|
@ -239,19 +304,37 @@ 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))
|
||||
elif '@PIN' in sms:
|
||||
user_setting_write(int_id(_rf_src), re.sub(' .*|@','',sms), int(re.sub('@PIN |@PIN','',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.strftime('%H:%M:%S - %m/%d/%y'), re.sub('@BB|@BB ','',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:
|
||||
email_message = re.sub('.*@|.* E-', '', sms)
|
||||
to_email = re.sub(' E-.*', '', sms)
|
||||
email_message = str(re.sub('.*@|.* E-', '', sms))
|
||||
to_email = str(re.sub(' E-.*', '', sms))
|
||||
email_subject = 'New message from ' + str(get_alias(int_id(_rf_src), subscriber_ids))
|
||||
logger.info('Email to: ' + to_email)
|
||||
logger.info('Message: ' + email_message)
|
||||
try:
|
||||
send_email(to_email, email_subject, email_message)
|
||||
logger.info('Email sent.')
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Failed to send email.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
elif '@SOS' in sms or '@NOTICE' in sms:
|
||||
sos_write(int_id(_rf_src), time.time(), sms)
|
||||
elif '@REM SOS' == sms:
|
||||
os.remove(emergency_sos_file)
|
||||
logger.info('Removing SOS or Notice')
|
||||
elif '@' and 'M-' in sms:
|
||||
message = re.sub('^@|.* M-|','',sms)
|
||||
recipient = re.sub('@| M-.*','',sms)
|
||||
mailbox_write(get_alias(int_id(_rf_src), subscriber_ids), int_id(_rf_src), time.time(), message, str(recipient).upper())
|
||||
elif '@REM MAIL' == sms:
|
||||
mailbox_delete(_rf_src)
|
||||
elif '@MH' in sms:
|
||||
grid_square = re.sub('@MH ', '', sms)
|
||||
if len(grid_square) < 6:
|
||||
|
@ -275,7 +358,7 @@ def process_sms(_rf_src, sms):
|
|||
logger.info('Latitude: ' + str(aprs_lat))
|
||||
logger.info('Longitude: ' + str(aprs_lon))
|
||||
# 14FRS2013 simplified and moved settings retrieval
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
|
@ -296,7 +379,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)
|
||||
|
@ -304,19 +387,21 @@ def process_sms(_rf_src, sms):
|
|||
try:
|
||||
aprslib.parse(aprs_loc_packet)
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, time.strftime('%H:%M:%S - %m/%d/%y'))
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, time.time(), comment)
|
||||
#logger.info('Sent manual position to APRS')
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Exception. Not uploaded')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
packet_assembly = ''
|
||||
|
||||
|
||||
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())
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
if int_id(_rf_src) in user_settings and user_settings[int_id(_rf_src)][1]['ssid'] != '':
|
||||
ssid = user_settings[int_id(_rf_src)][1]['ssid']
|
||||
else:
|
||||
|
@ -327,16 +412,20 @@ def process_sms(_rf_src, sms):
|
|||
aprslib.parse(aprs_msg_pkt)
|
||||
aprs_send(aprs_msg_pkt)
|
||||
#logger.info('Packet sent.')
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Error uploading MSG packet.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
try:
|
||||
if sms in cmd_list:
|
||||
logger.info('Executing command/script.')
|
||||
os.popen(cmd_list[sms]).read()
|
||||
packet_assembly = ''
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Exception. Command possibly not in list, or other error.')
|
||||
packet_assembly = ''
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
else:
|
||||
pass
|
||||
|
||||
|
@ -394,13 +483,18 @@ class DATA_SYSTEM(HBSYSTEM):
|
|||
lon_min = ba2num(bptc_decode(_data)[46:52])
|
||||
lat_min_dec = str(ba2num(bptc_decode(_data)[24:38])).zfill(4)
|
||||
lon_min_dec = str(ba2num(bptc_decode(_data)[52:66])).zfill(4)
|
||||
aprs_lat = str(str(lat_deg) + str(lat_min) + '.' + str(lat_min_dec)[0:2]).zfill(7) + lat_dir
|
||||
aprs_lon = str(str(lon_deg) + str(lon_min) + '.' + str(lon_min_dec)[0:2]).zfill(8) + lon_dir
|
||||
# Old MD-380 coordinate format, keep here until new is confirmed working.
|
||||
#aprs_lat = str(str(lat_deg) + str(lat_min) + '.' + str(lat_min_dec)[0:2]).zfill(7) + lat_dir
|
||||
#aprs_lon = str(str(lon_deg) + str(lon_min) + '.' + str(lon_min_dec)[0:2]).zfill(8) + lon_dir
|
||||
# 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))
|
||||
# 14FRS2013 simplified and moved settings retrieval
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
|
@ -421,7 +515,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)
|
||||
|
@ -432,10 +526,12 @@ class DATA_SYSTEM(HBSYSTEM):
|
|||
float(lat_deg) < 91
|
||||
float(lon_deg) < 121
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, time.strftime('%H:%M:%S - %m/%d/%y'))
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, time.time(), comment)
|
||||
#logger.info('Sent APRS packet')
|
||||
except:
|
||||
except Exception as error_exception:
|
||||
logger.info('Error. Failed to send packet. Packet may be malformed.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
udt_block = 1
|
||||
hdr_type = ''
|
||||
else:
|
||||
|
@ -459,7 +555,7 @@ class DATA_SYSTEM(HBSYSTEM):
|
|||
btf = btf - 1
|
||||
logger.info('Block #: ' + str(btf))
|
||||
#logger.info(_seq)
|
||||
logger.info('Data block from ' + str(get_alias(int_id(_rf_src), subscriber_ids)) + '. DMR ID: ' + str(int_id(_rf_src)) + '. Destination: ' + str(int_id(_dst_id)))
|
||||
logger.info('Data block from ' + str(get_alias(int_id(_rf_src), subscriber_ids)) + '. DMR ID: ' + str(int_id(_rf_src)))
|
||||
logger.info(ahex(bptc_decode(_data)))
|
||||
if _seq == 0:
|
||||
n_packet_assembly = 0
|
||||
|
@ -474,7 +570,6 @@ class DATA_SYSTEM(HBSYSTEM):
|
|||
if btf == 0:
|
||||
final_packet = str(bitarray(re.sub("\)|\(|bitarray|'", '', packet_assembly)).tobytes().decode('utf-8', 'ignore'))
|
||||
sms_hex = str(ba2hx(bitarray(re.sub("\)|\(|bitarray|'", '', packet_assembly))))
|
||||
|
||||
sms_hex_string = re.sub("b'|'", '', str(sms_hex))
|
||||
#NMEA GPS sentence
|
||||
if '$GPRMC' in final_packet or '$GNRMC' in final_packet:
|
||||
|
@ -495,7 +590,7 @@ class DATA_SYSTEM(HBSYSTEM):
|
|||
# Begin APRS format and upload
|
||||
# Disable opening file for reading to reduce "collision" or reading and writing at same time.
|
||||
# 14FRS2013 simplified and moved settings retrieval
|
||||
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
if int_id(_rf_src) not in user_settings:
|
||||
ssid = str(user_ssid)
|
||||
icon_table = '/'
|
||||
|
@ -516,14 +611,16 @@ 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:
|
||||
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:
|
||||
# Try parse of APRS packet. If it fails, it will not upload to APRS-IS
|
||||
aprslib.parse(aprs_loc_packet)
|
||||
|
@ -531,9 +628,11 @@ class DATA_SYSTEM(HBSYSTEM):
|
|||
float(loc.lat)
|
||||
float(loc.lon)
|
||||
aprs_send(aprs_loc_packet)
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, str(loc.lat[0:7]) + str(loc.lat_dir), str(loc.lon[0:8]) + str(loc.lon_dir), time.strftime('%H:%M:%S - %m/%d/%y'))
|
||||
except:
|
||||
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, str(loc.lat[0:7]) + str(loc.lat_dir), str(loc.lon[0:8]) + str(loc.lon_dir), time.time(), comment)
|
||||
except Exception as error_exception:
|
||||
logger.info('Failed to parse packet. Packet may be deformed. Not uploaded.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
#final_packet = ''
|
||||
# Get callsign based on DMR ID
|
||||
# End APRS-IS upload
|
||||
|
@ -602,29 +701,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()
|
||||
|
||||
# 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)')
|
||||
|
@ -654,6 +731,46 @@ 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']
|
||||
|
||||
# User APRS settings
|
||||
user_settings_file = CONFIG['GPS_DATA']['USER_SETTINGS_FILE']
|
||||
|
||||
# Check if user_settings (for APRS settings of users) exists. Creat it if not.
|
||||
if Path(user_settings_file).is_file():
|
||||
pass
|
||||
else:
|
||||
Path(user_settings_file).touch()
|
||||
with open(user_settings_file, '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_mail_file:
|
||||
user_mail_file.write("[]")
|
||||
user_mail_file.close()
|
||||
|
||||
# Start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
|
|
|
@ -3,8 +3,8 @@ bitarray>=0.8.1
|
|||
Twisted>=16.3.0
|
||||
dmr_utils3>=0.1.19
|
||||
configparser>=3.0.0
|
||||
aprslib
|
||||
aprslib>=0.6.42
|
||||
pynmea2
|
||||
maidenhead
|
||||
flask
|
||||
smtplib
|
||||
folium
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
###############################################################################
|
||||
# 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
|
||||
###############################################################################
|
||||
|
||||
import aprslib
|
||||
import ast, os
|
||||
import re
|
||||
from configparser import ConfigParser
|
||||
import time
|
||||
import argparse
|
||||
|
||||
def mailbox_write(call, dmr_id, time, message, recipient):
|
||||
global mailbox_file
|
||||
mail_file = ast.literal_eval(os.popen('cat ' + parser.get('GPS_DATA', 'MAILBOX_FILE')).read())
|
||||
mail_file.insert(0, {'call': call, 'dmr_id': dmr_id, 'time': time, 'message':message, 'recipient': recipient})
|
||||
with open(parser.get('GPS_DATA', 'MAILBOX_FILE'), 'w') as mailbox_file:
|
||||
mailbox_file.write(str(mail_file[:100]))
|
||||
mailbox_file.close()
|
||||
print('User mail saved.')
|
||||
|
||||
def aprs_filter(packet):
|
||||
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).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'])
|
||||
if recipient == '':
|
||||
pass
|
||||
else:
|
||||
for i in user_settings.items():
|
||||
ssid = i[1][1]['ssid']
|
||||
if i[1][1]['ssid'] == '':
|
||||
ssid = user_aprs_ssid
|
||||
if recipient in i[1][0]['call'] and recipient_ssid in ssid:
|
||||
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')
|
||||
user_settings_file = parser.get('GPS_DATA', 'USER_SETTINGS_FILE')
|
||||
user_aprs_ssid = parser.get('GPS_DATA', 'USER_APRS_SSID')
|
||||
|
||||
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_file).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)
|
||||
|
||||
|
|
@ -21,9 +21,17 @@
|
|||
This is a web dashboard for the GPS/Data application.
|
||||
'''
|
||||
|
||||
from flask import Flask, render_template
|
||||
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__)
|
||||
|
||||
|
@ -38,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 = '''
|
||||
<tr>
|
||||
|
@ -52,25 +60,39 @@ def get_loc_data():
|
|||
<h2> <strong>Longitude</strong> </h2>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<h2> <strong>Local Time</strong> </h2>
|
||||
<h2> <strong>Time</strong> </h2>
|
||||
</td>
|
||||
</tr>
|
||||
'''
|
||||
last_known_loc_list = []
|
||||
display_number = 15
|
||||
for e in dash_loc:
|
||||
tmp_loc = tmp_loc + '''<tr>
|
||||
<td style="text-align: center;"><a href="https://aprs.fi/''' + e['call'] + '''"><strong>''' + e['call'] + '''</strong></a></td>
|
||||
if display_number == 0:
|
||||
break
|
||||
else:
|
||||
if e['call'] in last_known_loc_list:
|
||||
pass
|
||||
if e['call'] not in last_known_loc_list:
|
||||
if type(e['time']) == str:
|
||||
loc_time = str(e['time'])
|
||||
if type(e['time']) == int or type(e['time']) == float:
|
||||
loc_time = datetime.fromtimestamp(e['time']).strftime(time_format)
|
||||
last_known_loc_list.append(e['call'])
|
||||
display_number = display_number - 1
|
||||
tmp_loc = tmp_loc + '''<tr>
|
||||
<td style="text-align: center;"><a href="view_map?track=''' + e['call'] + '''"target="_blank"><strong>''' + e['call'] + '''</strong></a></td>
|
||||
<td style="text-align: center;"><strong> ''' + str(e['lat']) + ''' </strong></td>
|
||||
<td style="text-align: center;"><strong> ''' + str(e['lon']) + ''' </strong></td>
|
||||
<td style="text-align: center;"> ''' + e['time'] + ''' </td>
|
||||
<td style="text-align: center;"> ''' + loc_time + ''' </td>
|
||||
</tr>'''
|
||||
return str(str('<h1 style="text-align: center;">Positions Received</h1>') + tbl_hdr + loc_hdr + tmp_loc + tbl_ftr)
|
||||
return str(str('<h1 style="text-align: center;">Last Known Location</h1>') + tbl_hdr + loc_hdr + tmp_loc + tbl_ftr)
|
||||
except:
|
||||
return str('<h1 style="text-align: center;">No data</h1>')
|
||||
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())
|
||||
dash_bb = ast.literal_eval(os.popen('cat ' + bb_file).read())
|
||||
tmp_bb = ''
|
||||
|
||||
bb_hdr = '''
|
||||
|
@ -79,33 +101,104 @@ def get_bb_data():
|
|||
<h2><strong> Callsign </strong></h2>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<h2> <strong>DMR ID</strong> </h2>
|
||||
<h2> <strong>ID</strong> </h2>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<h2> <strong>Bulletin</strong> </h2>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<h2> <strong>Local Time</strong> </h2>
|
||||
<h2> <strong>Time</strong> </h2>
|
||||
</td>
|
||||
</tr>
|
||||
'''
|
||||
display_number = 10
|
||||
|
||||
for e in dash_bb:
|
||||
tmp_bb = tmp_bb + '''<tr>
|
||||
<td style="text-align: center;"><strong> ''' + e['call'] + ''' </strong></td>
|
||||
<td style="text-align: center;">''' + str(e['dmr_id']) + '''</td>
|
||||
<td style="text-align: center;"><strong> ''' + e['bulliten'] + ''' </strong></td>
|
||||
<td style="text-align: center;"> ''' + e['time'] + ''' </td>
|
||||
</tr>'''
|
||||
if display_number == 0:
|
||||
break
|
||||
else:
|
||||
if type(e['time']) == str:
|
||||
loc_time = str(e['time'])
|
||||
if type(e['time']) == int or type(e['time']) == float:
|
||||
loc_time = datetime.fromtimestamp(e['time']).strftime(time_format)
|
||||
display_number = display_number - 1
|
||||
tmp_bb = tmp_bb + '''<tr>
|
||||
<td style="text-align: center;"><strong> ''' + e['call'] + ''' </strong></td>
|
||||
<td style="text-align: center;">''' + str(e['dmr_id']) + '''</td>
|
||||
<td style="text-align: center;"><strong> ''' + e['bulletin'] + ''' </strong></td>
|
||||
<td style="text-align: center;"> ''' + loc_time + ''' </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>')
|
||||
|
||||
def check_emergency():
|
||||
# open emergency txt
|
||||
try:
|
||||
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:
|
||||
loc_time = datetime.fromtimestamp(sos_file['time']).strftime(time_format)
|
||||
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>'
|
||||
value = Markup("""
|
||||
<h1 style="text-align: center;">""" + notice_header + """</h1>
|
||||
<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'] + """</strong> - """ + str(sos_file['dmr_id']) + """</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;">""" + sos_file['message'] + """</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;">""" + loc_time + """</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </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>
|
||||
<p style="text-align: center;"><a href="https://aprs.fi/""" + sos_file['call'] + """"><strong>View on aprs.fi</strong></a></p>
|
||||
<hr />
|
||||
|
||||
""")
|
||||
return value
|
||||
except Exception as e:
|
||||
return ''
|
||||
|
||||
def aprs_to_latlon(x):
|
||||
degrees = int(x) // 100
|
||||
minutes = x - 100*degrees
|
||||
return degrees + minutes/60
|
||||
|
||||
def user_setting_write(dmr_id, input_ssid, input_icon, input_comment):
|
||||
dmr_id = int(dmr_id)
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
new_dict = user_settings
|
||||
new_dict[dmr_id][1]['ssid'] = input_ssid
|
||||
new_dict[dmr_id][2]['icon'] = input_icon
|
||||
new_dict[dmr_id][3]['comment'] = input_comment
|
||||
print(input_comment)
|
||||
print(new_dict[dmr_id])
|
||||
|
||||
# Write modified dict to file
|
||||
with open(user_settings_file, 'w') as user_dict_file:
|
||||
user_dict_file.write(str(new_dict))
|
||||
user_dict_file.close()
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
value = Markup('<strong>The HTML String</strong>')
|
||||
#return get_data()
|
||||
return render_template('index.html', title = dashboard_title, logo = logo)
|
||||
return render_template('index.html', title = dashboard_title, logo = logo, emergency = check_emergency())
|
||||
@app.route('/bulletin_board')
|
||||
def dash_bb():
|
||||
return get_bb_data()
|
||||
|
@ -113,10 +206,7 @@ def dash_bb():
|
|||
@app.route('/positions')
|
||||
def dash_loc():
|
||||
return get_loc_data()
|
||||
#return render_template('index.html', data = str(get_data()))
|
||||
##@app.route('/<string:page_name>/')
|
||||
##def render_static(page_name):
|
||||
## return render_template('%s.html' % page_name, title = dashboard_title, logo = logo, description = description)
|
||||
|
||||
@app.route('/help/')
|
||||
def help():
|
||||
#return get_data()
|
||||
|
@ -125,6 +215,550 @@ def help():
|
|||
def about():
|
||||
#return get_data()
|
||||
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)
|
||||
@app.route('/view_map')
|
||||
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 ' + loc_file).read())
|
||||
last_known_list = []
|
||||
coord_list = []
|
||||
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 type(user_coord['time']) == str:
|
||||
loc_time = str(user_coord['time'])
|
||||
if type(user_coord['time']) == int or type(user_coord['time']) == float:
|
||||
loc_time = datetime.fromtimestamp(user_coord['time']).strftime(time_format)
|
||||
if 'S' in user_coord['lat']:
|
||||
user_lat = -user_lat
|
||||
if 'W' in user_coord['lon']:
|
||||
user_lon = -user_lon
|
||||
loc_comment = ''
|
||||
if 'comment' in user_coord:
|
||||
loc_comment = """
|
||||
<tr>
|
||||
<td style="text-align: center;">Comment:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;"><strong>"""+ str(user_coord['comment']) +"""</strong></td>
|
||||
</tr>"""
|
||||
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=map_theme, 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>"""+ loc_time +"""</em></td>
|
||||
""" + loc_comment + """
|
||||
</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>""" + loc_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
|
||||
if not map_size:
|
||||
map_view = '''<table style="width: 1000px; height: 600px; margin-left: auto; margin-right: auto;" border="1">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
''' + folium_map._repr_html_() + '''</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>'''
|
||||
if map_size == 'full':
|
||||
map_view = folium_map._repr_html_()
|
||||
|
||||
content = '''
|
||||
<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>
|
||||
<p style="text-align: center;"><button onclick="self.close()">Close</button><!--<button onclick="history.back()">Back</button>-->
|
||||
</p>
|
||||
""" + map_view
|
||||
return render_template('generic.html', title = dashboard_title, logo = logo, content = Markup(content))
|
||||
except Exception as e:
|
||||
content = """<h1 style="text-align: center;">Station not found.</h1>
|
||||
#<p style="text-align: center;"><button onclick="self.close()">Close Window</button>
|
||||
#</p>"""
|
||||
return render_template('generic.html', title = dashboard_title, logo = logo, content = Markup(content))
|
||||
|
||||
if not track_call:
|
||||
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'])))
|
||||
user_lon = aprs_to_latlon(float(re.sub('[A-Za-z]','', user_coord['lon'])))
|
||||
if type(user_coord['time']) == str:
|
||||
loc_time = str(user_coord['time'])
|
||||
if type(user_coord['time']) == int or type(user_coord['time']) == float:
|
||||
loc_time = datetime.fromtimestamp(user_coord['time']).strftime(time_format)
|
||||
if 'S' in user_coord['lat']:
|
||||
user_lat = -user_lat
|
||||
if 'W' in user_coord['lon']:
|
||||
user_lon = -user_lon
|
||||
loc_comment = ''
|
||||
coord_list.append([user_lat, user_lon])
|
||||
if 'comment' in user_coord:
|
||||
loc_comment = """
|
||||
<tr>
|
||||
<td style="text-align: center;">Comment:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;"><strong>"""+ str(user_coord['comment']) +"""</strong></td>
|
||||
</tr>"""
|
||||
if user_coord['call'] not in last_known_list:
|
||||
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>""" + loc_time + """</em></td>
|
||||
</tr>
|
||||
""" + loc_comment + """
|
||||
<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)
|
||||
last_known_list.append(user_coord['call'])
|
||||
if user_coord['call'] in last_known_list:
|
||||
if coord_list.count([user_lat, user_lon]) > 15:
|
||||
pass
|
||||
else:
|
||||
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>""" + loc_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_()
|
||||
|
||||
@app.route('/map/')
|
||||
def map():
|
||||
return render_template('map.html', title = dashboard_title, logo = logo)
|
||||
|
||||
@app.route('/user', methods = ['GET', 'POST'])
|
||||
def user_settings():
|
||||
user_settings = ast.literal_eval(os.popen('cat ' + user_settings_file).read())
|
||||
user_id = request.args.get('user_id')
|
||||
if request.method == 'POST' and request.form.get('dmr_id'):
|
||||
if int(request.form.get('dmr_id')) in user_settings:
|
||||
user_id = request.form.get('dmr_id')
|
||||
ssid = user_settings[int(request.form.get('dmr_id'))][1]['ssid']
|
||||
icon = user_settings[int(request.form.get('dmr_id'))][2]['icon']
|
||||
comment = user_settings[int(request.form.get('dmr_id'))][3]['comment']
|
||||
try:
|
||||
pin = user_settings[int(request.form.get('dmr_id'))][4]['pin']
|
||||
if ssid == '':
|
||||
ssid = aprs_ssid
|
||||
if icon == '':
|
||||
icon = '\['
|
||||
if comment == '':
|
||||
comment = default_comment + ' ' + user_id
|
||||
user_result = """
|
||||
Use this tool to change the stored APRS settings for your DMR ID. When a position is sent, the stored settings will be used to format the APRS packet. Leave field(s) blank for default value.
|
||||
<h2 style="text-align: center;"> Modify Settings for ID: """ + user_id + """</h2>
|
||||
<form action="user" method="post">
|
||||
<table style="margin-left: auto; margin-right: auto; width: 419.367px;" border="1">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>Callsign:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;"><strong>""" + str(user_settings[int(user_id)][0]['call']) + """</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>SSID:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;"><input id="ssid" name="ssid" type="text" placeholder='""" + ssid + """' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>Icon:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;"><input id="icon" name="icon" type="text" placeholder='""" + icon + """' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>Comment:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;"><input id="comment" name="comment" type="text" placeholder='""" + comment + """'/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>DMR ID:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;"><input id="dmr_id" name="dmr_id" type="text" value='""" + user_id + """'/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>PIN:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;"><input id="pin" name="pin" type="password" /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="text-align: center;"><input type="submit" value="Submit" /></p>
|
||||
</form>
|
||||
<p> </p>
|
||||
|
||||
|
||||
"""
|
||||
except:
|
||||
user_result = """<h2 style="text-align: center;">No PIN set for """ + str(user_settings[int(user_id)][0]['call']) + """ - """ + request.form.get('dmr_id') + """</h2>
|
||||
<p style="text-align: center;"><button onclick="history.back()">Back</button>
|
||||
</p>"""
|
||||
if int(request.form.get('dmr_id')) not in user_settings:
|
||||
user_result = """<h2 style="text-align: center;">DMR ID not found.</h2>
|
||||
<p style="text-align: center;"><button onclick="history.back()">Back</button>
|
||||
</p>"""
|
||||
#if edit_user:
|
||||
|
||||
if request.method == 'POST' and request.form.get('dmr_id') and request.form.get('pin'):
|
||||
if int(request.form.get('pin')) == pin:
|
||||
ssid = request.form.get('ssid')
|
||||
icon = request.form.get('icon')
|
||||
comment = request.form.get('comment')
|
||||
user_setting_write(request.form.get('dmr_id'), request.form.get('ssid'), request.form.get('icon'), request.form.get('comment'))
|
||||
user_result = """<h2 style="text-align: center;">Changed settings for """ + str(user_settings[int(user_id)][0]['call']) + """ - """ + request.form.get('dmr_id') + """</h2>
|
||||
<p style="text-align: center;"><button onclick="history.back()">Back</button>
|
||||
</p>"""
|
||||
if int(request.form.get('pin')) != pin:
|
||||
user_result = """<h2 style="text-align: center;">Incorrect PIN.</h2>
|
||||
<p style="text-align: center;"><button onclick="history.back()">Back</button>
|
||||
</p>"""
|
||||
|
||||
if request.method == 'GET' and not request.args.get('user_id'):
|
||||
user_result = """
|
||||
Use this tool to find, check, and change the stored APRS settings for your DMR ID. When a position is sent, the stored settings will be used to format the APRS packet.
|
||||
<table style="width: 600px; margin-left: auto; margin-right: auto;" border="3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><form action="user" method="get">
|
||||
<table style="margin-left: auto; margin-right: auto;">
|
||||
<tbody>
|
||||
<tr style="height: 62px;">
|
||||
<td style="text-align: center; height: 62px;">
|
||||
<h2><strong><label for="user_id">Look up DMR ID:</label></strong></h2>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="height: 51.1667px;">
|
||||
<td style="height: 51.1667px;"><input id="user_id" name="user_id" type="text" /></td>
|
||||
</tr>
|
||||
<tr style="height: 27px;">
|
||||
<td style="text-align: center; height: 27px;"><input type="submit" value="Submit" /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form></td>
|
||||
<td><form action="user" method="post">
|
||||
<table style="margin-left: auto; margin-right: auto;">
|
||||
<tbody>
|
||||
<tr style="height: 62px;">
|
||||
<td style="text-align: center; height: 62px;">
|
||||
<h2><strong><label for="dmr_id">Edit DMR ID:</label></strong></h2>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="height: 51.1667px;">
|
||||
<td style="height: 51.1667px;"><input id="dmr_id" name="dmr_id" type="text" /></td>
|
||||
</tr>
|
||||
<tr style="height: 27px;">
|
||||
<td style="text-align: center; height: 27px;"><input type="submit" value="Submit" /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
|
||||
|
||||
"""
|
||||
#else:
|
||||
if request.method == 'GET' and request.args.get('user_id'):
|
||||
try:
|
||||
call = user_settings[int(user_id)][0]['call']
|
||||
ssid = user_settings[int(user_id)][1]['ssid']
|
||||
icon = user_settings[int(user_id)][2]['icon']
|
||||
comment = user_settings[int(user_id)][3]['comment']
|
||||
if ssid == '':
|
||||
ssid = aprs_ssid
|
||||
if icon == '':
|
||||
icon = '\['
|
||||
if comment == '':
|
||||
comment = default_comment + ' ' + user_id
|
||||
#for result in user_settings:
|
||||
#return user_settings[int(user_id)][0]
|
||||
#return user_id
|
||||
#return user_settings
|
||||
user_result = """<h2 style="text-align: center;"> Settings for ID: """ + user_id + """</h2>
|
||||
<table style="margin-left: auto; margin-right: auto; width: 419.367px;" border="1">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>Callsign:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;">""" + user_settings[int(user_id)][0]['call'] + """</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>SSID:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;">""" + ssid + """</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>Icon:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;">""" + icon + """</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 82px;"><strong>Comment:</strong></td>
|
||||
<td style="width: 319.367px; text-align: center;">""" + comment + """</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="text-align: center;"><button onclick="history.back()">Back</button>
|
||||
</p>
|
||||
"""
|
||||
except:
|
||||
user_result = '''<h2 style="text-align: center;">User ID not found.</h2>
|
||||
<p style="text-align: center;"><button onclick="history.back()">Back</button>
|
||||
</p>'''
|
||||
|
||||
return render_template('generic.html', title = dashboard_title, logo = logo, content = Markup(user_result))
|
||||
|
||||
@app.route('/mailbox')
|
||||
def mailbox():
|
||||
recipient = request.args.get('recipient')
|
||||
if not recipient:
|
||||
mail_content = """
|
||||
<p>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 <strong>""" + data_call_id + """</strong>.
|
||||
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 (without APRS SSID) below to check for messages. See the <a href="help">help</a> page for more information.</p>
|
||||
<form action="mailbox" method="get">
|
||||
<table style="margin-left: auto; margin-right: auto;">
|
||||
<tbody>
|
||||
<tr style="height: 62px;">
|
||||
<td style="text-align: center; height: 62px;">
|
||||
<h2><strong><label for="recipient">Callsign:</label></strong></h2>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="height: 51.1667px;">
|
||||
<td style="height: 51.1667px;"><input id="recipient" name="recipient" type="text" /></td>
|
||||
</tr>
|
||||
<tr style="height: 27px;">
|
||||
<td style="text-align: center; height: 27px;"><input type="submit" value="Submit" /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<p> </p>
|
||||
|
||||
"""
|
||||
|
||||
else:
|
||||
mailbox_file = ast.literal_eval(os.popen('cat ' + the_mailbox_file).read())
|
||||
mail_content = '<h2 style="text-align: center;">Messages for: ' + recipient.upper() + '''
|
||||
</h2>\n<p style="text-align: center;"><button onclick="history.back()">Back</button></p>\n
|
||||
<h4 style="text-align: center;"><a href="mailbox_rss?recipient=''' + recipient.upper() + '''"><em>Mailbox RSS Feed for ''' + recipient.upper() + '''</em></a><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5QIcFBAOXAevLAAAAZZJREFUSMftlbtKA0EUhj8jWhi8gaIEC29oxEoRFESLgIXYiWVSKoj6CCrBBwj6CBHNE1hEWy21ETQqiIW1wXhPo81ZOBw2apbdVPvDsDPnP8M/5zKzECJEQKivYO8DFoAYEAGKtTpQEvhW4w3IA+tAVy2F9fgEskA8COHUL8LOKAMZoMmLQF0FewcwImmNAzPANBB18b0BFoGroNLfBiyLgI2+BMwF3XgNwCrwYsQ//BBPSRPdAoeybjE+A8ClS+Sjfnf1E5A2dW4FzoxfwWvD/XWd7oAxI24jz3gVnpS7eiEpt+KvQEL5D5qal/245zFgU+pnXzMd+Zrh9/3q5l7g3CXtTs0bgWvFffn5vDa7iKcVv2K4DS8i3cAOsAuMm8h12ovqqrVL/R3upFrRKPBgHgctvm0iSynuWNnf5bf6byy5dPKe4nukhg6XU9yW2TfsJlDpNCUX27OaP8pD4WBCzQtmX381EUeAI3Xqe6m5xoHpYAezJuJkNb9Fh0tI4+SlXhpTwJBaZ+XbCcwr+6kcPESI2uAHmAijFaMnEmYAAAAASUVORK5CYII=" /></h4>
|
||||
'''
|
||||
for messages in mailbox_file:
|
||||
if messages['recipient'] == recipient.upper():
|
||||
sender = """
|
||||
<tr>
|
||||
<td style="width: 63px;"><strong>DMR ID:</strong></td>
|
||||
<td style="width: 292.55px; text-align: center;">""" + str(messages['dmr_id']) + """</td>
|
||||
</tr>
|
||||
"""
|
||||
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 = """
|
||||
<tr>
|
||||
<td style="width: 63px;"><strong>APRS Call:</strong></td>
|
||||
<td style="width: 292.55px; text-align: center;">""" + str(messages['dmr_id']) + """</td>
|
||||
</tr>
|
||||
"""
|
||||
mail_content = mail_content + """
|
||||
<table style="margin-left: auto; margin-right: auto; width: 372.55px;" border="1">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 63px;"><strong>From:</strong></td>
|
||||
<td style="text-align: center; width: 292.55px;"><strong>""" + messages['call'] + """</strong></td>
|
||||
</tr>
|
||||
""" + sender + """
|
||||
<tr>
|
||||
<td style="width: 63px;"><strong>Time:</strong></td>
|
||||
<td style="width: 292.55px; text-align: center;">""" + loc_time + """</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 63px;"><strong>Message:</strong></td>
|
||||
<td style="width: 292.55px; text-align: center;"><strong>""" + messages['message'] + """</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
|
||||
"""
|
||||
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 ' + bb_file).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:
|
||||
if type(entry['time']) == str:
|
||||
loc_time = str(entry['time'])
|
||||
if type(entry['time']) == int or type(entry['time']) == float:
|
||||
loc_time = datetime.fromtimestamp(entry['time']).strftime(time_format)
|
||||
post_data = post_data + """
|
||||
<item>
|
||||
<title>""" + entry['call'] + ' - ' + str(entry['dmr_id']) + """</title>
|
||||
<link>""" + rss_link + """</link>
|
||||
<description>""" + entry['bulletin'] + """ - """ + loc_time + """</description>
|
||||
<pubDate>""" + datetime.fromtimestamp(entry['time']).strftime('%a, %d %b %y') +"""</pubDate>
|
||||
</item>
|
||||
"""
|
||||
return Response(rss_header + post_data + "\n</channel>\n</rss>", mimetype='text/xml')
|
||||
except Exception as e:
|
||||
#return str('<h1 style="text-align: center;">No data</h1>')
|
||||
return str(e)
|
||||
|
||||
@app.route('/mailbox_rss')
|
||||
def mail_rss():
|
||||
mailbox_file = ast.literal_eval(os.popen('cat ' + the_mailbox_file).read())
|
||||
post_data = ''
|
||||
recipient = request.args.get('recipient').upper()
|
||||
rss_header = """<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>""" + dashboard_title + """ - Mailbox Feed for """ + recipient + """</title>
|
||||
<link>""" + rss_link + """</link>
|
||||
<description>This is a Mailbox feed from """ + dashboard_title + """ for """ + recipient + """.</description>"""
|
||||
for entry in mailbox_file:
|
||||
if type(entry['time']) == str:
|
||||
loc_time = str(entry['time'])
|
||||
if type(entry['time']) == int or type(entry['time']) == float:
|
||||
loc_time = datetime.fromtimestamp(entry['time']).strftime(time_format)
|
||||
if entry['recipient'] == recipient:
|
||||
post_data = post_data + """
|
||||
<item>
|
||||
<title>""" + entry['call'] + ' - ' + str(entry['dmr_id']) + """</title>
|
||||
<link>""" + rss_link + """</link>
|
||||
<description>""" + entry['message'] + """ - """ + loc_time + """</description>
|
||||
<pubDate>""" + datetime.fromtimestamp(entry['time']).strftime('%a, %d %b %y') +"""</pubDate>
|
||||
</item>
|
||||
"""
|
||||
return Response(rss_header + post_data + "\n</channel>\n</rss>", 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')
|
||||
user_settings_file = parser.get('GPS_DATA', 'USER_SETTINGS_FILE')
|
||||
########################
|
||||
|
||||
app.run(debug = True, port=dash_port, host=dash_host)
|
||||
|
|
|
@ -47,3 +47,17 @@ 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 = (47.00, -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'
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div>
|
||||
<hr />
|
||||
<div style="text-align: center;">Dashboard created by KF7EEL - <a href="https://github.com/kf7eel/hblink3">https://github.com/kf7eel/hblink3</a><br />HBLink created by N0MJS.<br /><a href="https://github.com/HBLink-org">HBLink GitHub page</a><br />This is branch: <strong>gps<strong></div>
|
||||
<div style="text-align: center;">Dashboard created by KF7EEL - <a href="https://kf7eel.github.io/hblink3/">https://github.com/kf7eel/hblink3</a><br />HBLink created by N0MJS.<br /><a href="https://github.com/HBLink-org">HBLink GitHub page</a><br />This is branch: <strong>gps<strong></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{% include 'page.html' %}
|
||||
{% include 'header.html' %}
|
||||
{{content}}
|
||||
{% include 'footer.html' %}
|
||||
|
|
@ -4,10 +4,14 @@
|
|||
<table style="width: 500px; margin-left: auto; margin-right: auto;" border="black" cellspacing="3" cellpadding="3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align: center;"><button onclick="window.location.href='/';"> D-APRS Dashboard </button></td>
|
||||
<td style="text-align: center;"><button onclick="window.location.href='/help';"> D-APRS Help </button></td>
|
||||
<td style="text-align: center;"><button onclick="window.location.href='/';"> Dashboard Home </button></td>
|
||||
<td style="text-align: center;"><button onclick="window.location.href='/map';"> Station Map </button></td>
|
||||
<td style="text-align: center;"><button onclick="window.location.href='/mailbox';"> The Mailbox </button></td>
|
||||
<td style="text-align: center;"><button onclick="window.location.href='/user';"> User Settings </button></td>
|
||||
<td style="text-align: center;"><button onclick="window.location.href='/help';"> Dashboard Help </button></td>
|
||||
<td style="text-align: center;"><button onclick="window.location.href='/about';"> Gateway Contact </button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
|
||||
|
|
|
@ -51,6 +51,11 @@
|
|||
<td><em><code>@SSID 7</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@PIN</strong></td>
|
||||
<td>Set a PIN. This is used for changing your APRS settings ia the dashboard. You must set this for each DMR ID you wish to change via the dashboard.</td>
|
||||
<td><em><code>@PIN 1234</code></em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@MH</strong></td>
|
||||
<td>Set you location by maidenhead grid square. Designed for radios with no GPS or that are not compatable yet.</td>
|
||||
<td><em><code>@MH DN97uk</code></em></td>
|
||||
|
@ -69,11 +74,44 @@
|
|||
<td><strong>[EMAIL ADDRESS] E-[MESSAGE]</strong></td>
|
||||
<td>Send an email to an email address.</td>
|
||||
<td><em><code>test@example.org E-This is a test.</code></em></td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@[RECIPIENT CALLSIGN] M-[MESSAGE]</strong></td>
|
||||
<td>Post a message to the Mailbox.</td>
|
||||
<td><em>@N0CALL M-This is a test.</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@REM MAIL</strong></td>
|
||||
<td>Remove all mail addressed to you.</td>
|
||||
<td><em>@REM MAIL</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@SOS</strong></td>
|
||||
<td>EMERGENCY activation displays a banner on the main dashboard page.</td>
|
||||
<td><em>THIS IS AN EMERGENCY. @SOS</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@NOTICE</strong></td>
|
||||
<td>Used to prominantly display a notice, similar to EMERGENCY activation.</td>
|
||||
<td><em>@NOTICE Server going down at 1800</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>@REM SOS</strong></td>
|
||||
<td>Removes an EMERGENCY or notice.</td>
|
||||
<td><em>@REM SOS</em></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
<p><strong>Commands should be in upper case.</strong> Send an SMS to <strong>{{data_call_id}}</strong> as a <strong>{{data_call_type}}</strong> with the desired command followed by the value. For example, to change your icon to a dog, the command would be <code>@ICON /p</code> (see the icon table for values). Changing your SSID is as simple as <code>@SSID 7</code>, and <code>@COM Testing 123</code> will change the comment.</p>
|
||||
<p>Sending <code>@BB Test</code> will result in a post to the bulletin board with the messaage of "Test".</p>
|
||||
<p><strong>To remove any of the stored values, just send the appropriate command without any input.</strong> <code>@COM</code> will remove the stored comment, <code>@ICON</code> will remove the stored icon, and <code>@ICON</code> will remove the strored icon. Any position reports sent will have the default settings.</p>
|
||||
|
||||
<h3 style="text-align: center;">EMERGENCY Activation</h3>
|
||||
<p>The EMERGENCY Activation function will display a prominant banner accross the main dashboard page. The banner contains a message, time, callsign of station, and a link to the last known location of the station. This feature is intended to be used in situations where a user is in distress. To trigger an EMERGENCY activation, simply send an SMS to <strong>{{data_call_id}}</strong> with <strong>@SOS</strong> in the message. The <strong>@SOS</strong> command can be anywhere in the message. A less intimidating banner can be displayed for testing, or other purposes, by using <strong>@NOTICE</strong>. To remove the EMERGENCY activation, simply send the <strong>@REM SOS</strong> command.</p>
|
||||
<h3 style="text-align: center;">The Map</h3>
|
||||
<p>This dashboard now has a built in map. The map will display the last know location of stations and some previous plots from them. You can track a station by clicking on the red pin, and selecting the <strong>Track Station</strong> link. When tracking a station, you have a few options for a refresh rate, located in a drop down menu.</p>
|
||||
<h3 style="text-align: center;">The Mailbox</h3>
|
||||
<p>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 formatted SMS to <strong>{{data_call_id}}</strong>. See the table above for a syntax example of posting a message to the Mailbox. The message recipient can then use the mailbox to check for messages. Message recipients can delete all of their messages by sending the command <strong>@REM MAIL</strong>. There is no way to remove a single message, it's all or none.</p>
|
||||
|
||||
{% include 'footer.html' %}
|
||||
|
|
|
@ -16,13 +16,25 @@ p {
|
|||
max-width: 1200px;
|
||||
min-width: 1200px;
|
||||
margin: auto;
|
||||
}
|
||||
.emergency_button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
background-color: ##ff0000;
|
||||
padding: 14px 28px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<div class="content">
|
||||
<body>
|
||||
{% include 'header.html' %}
|
||||
{{emergency}}
|
||||
<p style="text-align: center;"><em>Page automatically reloads every 2 minutes.</em></p>
|
||||
<h4 style="text-align: center;"><a href="bulletin_rss.xml"><em>Bulletin Board RSS Feed</em></a><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5QIcFBAOXAevLAAAAZZJREFUSMftlbtKA0EUhj8jWhi8gaIEC29oxEoRFESLgIXYiWVSKoj6CCrBBwj6CBHNE1hEWy21ETQqiIW1wXhPo81ZOBw2apbdVPvDsDPnP8M/5zKzECJEQKivYO8DFoAYEAGKtTpQEvhW4w3IA+tAVy2F9fgEskA8COHUL8LOKAMZoMmLQF0FewcwImmNAzPANBB18b0BFoGroNLfBiyLgI2+BMwF3XgNwCrwYsQ//BBPSRPdAoeybjE+A8ClS+Sjfnf1E5A2dW4FzoxfwWvD/XWd7oAxI24jz3gVnpS7eiEpt+KvQEL5D5qal/245zFgU+pnXzMd+Zrh9/3q5l7g3CXtTs0bgWvFffn5vDa7iKcVv2K4DS8i3cAOsAuMm8h12ovqqrVL/R3upFrRKPBgHgctvm0iSynuWNnf5bf6byy5dPKe4nukhg6XU9yW2TfsJlDpNCUX27OaP8pD4WBCzQtmX381EUeAI3Xqe6m5xoHpYAezJuJkNb9Fh0tI4+SlXhpTwJBaZ+XbCcwr+6kcPESI2uAHmAijFaMnEmYAAAAASUVORK5CYII=" /></h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{% include 'page.html' %}
|
||||
{% include 'header.html' %}
|
||||
<p style="text-align: center;"><span style="color: #ff0000;"><strong>Red</strong></span>: Last location received. <span style="color: #0000ff;"><strong>Blue</strong></span>: Location history.</p>
|
||||
<p style="text-align: center;"> </p>
|
||||
<div><p align="center"><iframe style="border: none;" title="Map" src="/view_map?track=" width="1100" height="700"></iframe></p></div>
|
||||
<p> </p>
|
||||
{% include 'footer.html' %}
|
||||
|
Loading…
Reference in New Issue