132 Commits

Author SHA1 Message Date
KF7EEL dba4a5692d Update README.md 2021-02-08 14:55:08 -08:00
KF7EEL bcb55b7d72 disable dashboard stuff 2021-02-08 14:46:11 -08:00
KF7EEL 74ca23af73 add example config for combined application 2021-02-08 12:38:45 -08:00
KF7EEL 76cadf01a4 fix typo 2021-02-07 15:02:52 -08:00
KF7EEL ccd197a8c1 add combined bridge/D-APRS 2021-02-07 13:52:03 -08:00
KF7EEL 1a96434c78 fix typo 2021-02-04 12:58:16 -08:00
KF7EEL 3047b55bcc add 14FRS2013 contributions, add both option for call type 2021-02-04 12:45:14 -08:00
KF7EEL 21a0c95ad3 add option to disable APRS upload 2021-02-04 10:06:09 -08:00
Roby 08f53ee1a4 Update help.html (#22)
Add new commands
2021-02-03 13:37:54 -08:00
KF7EEL 82ce4995ae Merge branch 'gps' of https://github.com/kf7eel/hblink3 into gps 2021-02-02 12:04:19 -08:00
KF7EEL dc5dbc0f7f fix typo for GNRMC 2021-02-02 12:04:03 -08:00
KF7EEL 85d085d563 Update README.md 2021-02-02 11:57:24 -08:00
KF7EEL 5e349a9705 Merge branch 'gps' of https://github.com/kf7eel/hblink3 into gps 2021-02-02 11:36:17 -08:00
KF7EEL 664cc46b63 potential fix for RT73 2021-02-02 11:35:56 -08:00
KF7EEL 45dca64490 Update README.md 2021-02-01 17:31:19 -08:00
KF7EEL bc8bb8b298 Update README.md 2021-02-01 17:29:35 -08:00
KF7EEL 40fa5372ce change email syntax, bug fix 2021-02-01 17:28:11 -08:00
KF7EEL 586533ac4c Update README.md 2021-02-01 07:32:55 -08:00
KF7EEL f99451b0a4 Update gps_data-SAMPLE.cfg 2021-01-31 12:56:48 -08:00
KF7EEL afb249c418 fix bugs 2021-01-31 12:45:43 -08:00
KF7EEL a750dedab0 Merge branch 'gps' of https://github.com/kf7eel/hblink3 into gps 2021-01-31 12:36:35 -08:00
KF7EEL 84f7baf9b4 Update requirements.txt 2021-01-31 12:35:22 -08:00
KF7EEL fccbdc908c Update README.md 2021-01-31 12:34:58 -08:00
KF7EEL 2affa319d4 Update README.md 2021-01-31 12:33:57 -08:00
KF7EEL 54f4ad799e imrove email template 2021-01-31 11:55:09 -08:00
KF7EEL 372bb845b2 add email sending 2021-01-31 11:42:15 -08:00
KF7EEL fcb0f1bd05 fix manual positions 2021-01-31 09:17:45 -08:00
KF7EEL 14f4eafa6e Update footer.html 2021-01-30 11:22:53 -08:00
KF7EEL bebf487936 Update help.html 2021-01-30 11:18:28 -08:00
KF7EEL 9def4fbfec Update help.html 2021-01-30 09:58:10 -08:00
KF7EEL 33f499a32a Update gps_data.py 2021-01-30 09:15:18 -08:00
KF7EEL 2d94dbe045 fix MD-380 and SMS positions 2021-01-26 08:54:48 -08:00
KF7EEL 0b5ecaa04f Update README.md 2021-01-21 15:50:47 -08:00
KF7EEL becbffed8e Update README.md 2021-01-12 14:23:54 -08:00
KF7EEL 24e5329ac7 add sending messages to APRS 2021-01-12 13:54:53 -08:00
KF7EEL e7b76acaff fix typo 2021-01-12 10:00:38 -08:00
KF7EEL c9b9c307d6 add GNRMC to filter to allow RT73 compatability 2021-01-12 09:56:17 -08:00
M0GLJ 22d371a8f6 Update README.md (#19)
Model Correction
2021-01-10 10:01:49 -08:00
KF7EEL f81e5dae78 Update README.md 2021-01-08 10:19:28 -08:00
KF7EEL bd4bb0b611 Change example to allow D-APRS out of the box 2021-01-07 15:10:34 -08:00
KF7EEL d76a808e4a Update gps_data.py 2021-01-06 11:31:22 -08:00
KF7EEL e6e9600e73 fix grammar 2021-01-02 18:13:35 -08:00
KF7EEL 22e04f6ff3 fix dash width 2021-01-02 18:02:55 -08:00
KF7EEL a943748c94 fix missing templates 2021-01-02 16:17:34 -08:00
KF7EEL 674f9102d1 improve dash 2021-01-02 15:58:45 -08:00
KF7EEL 3dde015e5d Merge branch 'gps' of https://github.com/kf7eel/hblink3 into gps 2021-01-02 14:58:58 -08:00
KF7EEL 3b36b44c5d add page, split header and footer 2021-01-02 14:58:38 -08:00
KF7EEL e4ef4383fe Update gps_data-SAMPLE.cfg 2021-01-02 08:46:56 -08:00
KF7EEL f2675d885c clean up pages 2021-01-01 15:10:12 -08:00
KF7EEL 105f4bdc7a fix html 2021-01-01 14:46:05 -08:00
KF7EEL 1df3026a59 update dashboard theme 2021-01-01 14:44:08 -08:00
KF7EEL 79fe60fc3f add about page 2021-01-01 14:35:15 -08:00
KF7EEL 3a3f4719a0 fix description yet again 2021-01-01 14:16:57 -08:00
KF7EEL dcc1b4ff40 fix description again 2021-01-01 14:15:43 -08:00
KF7EEL e753f4f56b fix description location 2021-01-01 14:12:38 -08:00
KF7EEL 215af2ea6c add description to dashboard 2021-01-01 14:09:14 -08:00
KF7EEL 944e3bf1af Update requirements.txt 2021-01-01 13:51:45 -08:00
M0GLJ ecba8f75bd Added install.sh and appended requirements.txt (#16)
* Create install.sh

* Update requirements.txt

Add additional for gps operation
2021-01-01 13:51:20 -08:00
M0GLJ 1af61d184a Update README.md (#17) 2021-01-01 13:50:21 -08:00
KF7EEL f411531bd0 Merge branch 'gps' of https://github.com/kf7eel/hblink3 into gps 2021-01-01 09:58:33 -08:00
KF7EEL 15111563ed fix bug, improve index template 2021-01-01 09:58:13 -08:00
KF7EEL b6adc461c6 Update README.md 2021-01-01 09:43:19 -08:00
KF7EEL 6fc4dcfe27 Update README.md 2021-01-01 09:37:22 -08:00
KF7EEL 8cb40cbe2c add host to dashboard 2020-12-30 16:41:51 -08:00
KF7EEL 7cd73740b4 add port settings 2020-12-30 09:35:25 -08:00
KF7EEL 6da1c4de0d update web dashboard, add template 2020-12-30 09:10:23 -08:00
KF7EEL e4b55b05fe basic functioning web dashboard 2020-12-29 20:36:07 -08:00
KF7EEL 54bdbd72e8 Merge branch 'gps' of https://github.com/kf7eel/hblink3 into gps
add backend code for upcoming web dashboard
2020-12-27 20:39:42 -08:00
KF7EEL 3a8e111443 add backend code for upcoming web dashboard 2020-12-27 20:39:17 -08:00
KF7EEL a04f8e8cc3 Update README.md 2020-12-27 08:33:02 -08:00
KF7EEL 350b366ffd Update README.md 2020-12-26 15:30:58 -08:00
KF7EEL 636246c664 Update README.md 2020-12-26 14:47:52 -08:00
KF7EEL 2a752487d0 add UDT SAP requirement to reduce fake packets 2020-12-26 14:31:11 -08:00
KF7EEL c6ebf86b0f add UDT UAB criteria to reduce inaccurate packets 2020-12-26 13:43:19 -08:00
KF7EEL 4900f859f8 fix parse bug, md-380 working again 2020-12-26 12:53:50 -08:00
KF7EEL 17999eb3f4 fix typo 2020-12-26 10:38:03 -08:00
KF7EEL c09d2ea734 fix hdr_type error for non MD-380 radios 2020-12-26 09:59:08 -08:00
KF7EEL 84eaaf70f7 remove code from accidental commit 2020-12-24 13:53:44 -08:00
KF7EEL a0d49b5175 Update README.md 2020-12-23 22:59:52 -08:00
KF7EEL 423d269e78 Update README.md 2020-12-23 22:53:23 -08:00
KF7EEL 9c73c70d71 fix typo in gps_data.py 2020-12-23 16:09:45 -08:00
KF7EEL 11e5aba3fc fix typo 2020-12-23 15:27:59 -08:00
KF7EEL f6a501a42e initial commit for MD-380 type radios 2020-12-23 15:19:10 -08:00
KF7EEL 6902b3bd4c fix beacon time to minutes 2020-12-11 13:05:59 -08:00
KF7EEL fe1daeb406 add APRS beacon script for gps_data 2020-12-11 12:49:34 -08:00
KF7EEL 76ad6adea4 add APRS beacon script for gps_data 2020-12-11 12:47:43 -08:00
KF7EEL 542e7496ca fix import time 2020-12-06 16:39:05 -08:00
KF7EEL 05a90944e2 add official tocall, add timestamp, and hook for MD-380 GPS 2020-12-06 12:30:20 -08:00
KF7EEL 804cb1c025 Update README.md 2020-12-01 13:35:32 -08:00
KF7EEL b2fda94746 Update README.md 2020-12-01 13:34:39 -08:00
KF7EEL 6d934096aa Update README.md 2020-12-01 13:21:26 -08:00
KF7EEL a583ff92d2 parse all SMS for command string instead of header 2020-12-01 12:15:17 -08:00
KF7EEL 43e6e6d6b1 change handling of unknown SMS 2020-12-01 09:15:57 -08:00
KF7EEL 579ba3d282 add ETSI like SMS 2020-11-30 17:25:33 -08:00
KF7EEL 5c8be7c177 add debug statements 2020-11-30 14:08:33 -08:00
KF7EEL 28a2e7ba70 APRS position via SMS for grid square 2020-11-30 12:38:50 -08:00
KF7EEL b47d70142d Update README.md 2020-11-30 10:03:59 -08:00
KF7EEL 896ba4fbae change file read, move global declaration 2020-11-30 09:59:15 -08:00
KF7EEL d202f4d0ce fix group SMS for Motorola, fix typo 2020-11-29 15:36:44 -08:00
KF7EEL 4beaf3000d added blank variables to attempt to fix bug 2020-11-29 15:08:53 -08:00
KF7EEL 7e19e705ef Update README.md 2020-11-28 23:53:53 -08:00
KF7EEL 821c57b381 Update README.md 2020-11-28 19:57:37 -08:00
KF7EEL ffdfe55cb8 Merge branch 'gps' of https://github.com/kf7eel/hblink3 into gps 2020-11-28 09:15:26 -08:00
KF7EEL 9e40195161 attempt to fix bug with assembled packets 2020-11-28 09:14:26 -08:00
KF7EEL 7fd8500105 Update README.md 2020-11-27 23:53:22 -08:00
KF7EEL d9e5085765 Update README.md 2020-11-27 09:39:18 -08:00
KF7EEL d7afa94a5a added user settings, added speed and course 2020-11-27 09:31:38 -08:00
KF7EEL 00e3ea840c update readme, sample, and functions 2020-11-26 19:40:43 -08:00
KF7EEL 9dc7ce12e1 fix another bug 2020-11-26 17:44:19 -08:00
KF7EEL a21393313f revert change, remove UNIT_MAP changes 2020-11-26 15:23:33 -08:00
KF7EEL e736893543 time for static unit now generated at start 2020-11-26 11:50:28 -08:00
KF7EEL e88d545955 add hooks in code for shell commands 2020-11-25 15:53:42 -08:00
KF7EEL eca8619153 Merge branch 'gps' of https://github.com/kf7eel/hblink3 into gps 2020-11-25 14:52:41 -08:00
KF7EEL 8e8836057b fix typo for unit calls 2020-11-25 14:50:49 -08:00
KF7EEL 471feee9c3 implement static routes for unit calls 2020-11-25 14:39:28 -08:00
KF7EEL d4e1ebd4ec Update README.md 2020-11-25 12:35:06 -08:00
KF7EEL ee40ad1e34 Update README.md 2020-11-25 12:17:51 -08:00
KF7EEL 37521fc7f1 implement custom UNIT time 2020-11-25 12:02:58 -08:00
KF7EEL 64ee23788a Update README.md 2020-11-24 11:56:40 -08:00
KF7EEL c07edcdc91 ensure lat and lon are float 2020-11-23 13:43:54 -08:00
KF7EEL 29fb602fcb Update README.md 2020-11-23 13:19:04 -08:00
KF7EEL 6b9ae44256 add info to README 2020-11-23 13:15:22 -08:00
KF7EEL 72a9bd015b fix typo again 2020-11-23 12:54:44 -08:00
KF7EEL 0ffd119994 implement basic SMS processing 2020-11-23 12:52:40 -08:00
KF7EEL 0056e1e09e moved config to file 2020-11-23 10:08:31 -08:00
KF7EEL 84dee59b37 fix typo 2020-11-22 22:03:02 -08:00
KF7EEL 696e0bc128 fix typo 2020-11-22 21:58:45 -08:00
KF7EEL f233e50516 fix old data bug 2020-11-22 20:39:45 -08:00
KF7EEL 6293497b61 decode motorola type sms 2020-11-22 18:51:46 -08:00
KF7EEL 6c3cab0827 added debug stuff 2020-11-22 13:55:06 -08:00
KF7EEL 0c6912c55f fix typo 2020-11-22 12:44:52 -08:00
KF7EEL e838fc3423 now using NMEA parser 2020-11-22 12:43:38 -08:00
22 changed files with 231 additions and 1575 deletions
+2 -9
View File
@@ -1,13 +1,6 @@
## See the main README (under branch: gps) for more on the operation of the application.
# "Fully Featured" HBLink3
This is my version of HBLink3 that I use in "production." I have merged the private-call branch here. I have also merged a modified implementation of uploading APRS locations for connected peers here (modified to "per master" configuration. APRS peer implementation originally by **IU7IGU**). Finally, I have included the GPS/Data Application here to support APRS position reports from some GPS enabled radios. All of this leads to the following features:
* Working Unit calls (private calls)
* APRS position upload for connected repeaters and hotspots
* APRS position upload from some GPS enabled radios
**The same stuff (commands, settings, etc.) in the gps branch apply here as well.**
This branch is really no different than the other branches. It just has the dashboard disabled, and the example configs modified to make it easier to run on an Android device.
---
### FOR SUPPORT, DISCUSSION, GETTING INVOLVED ###
+1 -3
View File
@@ -42,7 +42,7 @@ from twisted.protocols.basic import NetstringReceiver
from twisted.internet import reactor, task
# Things we import from the main hblink module
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases, aprs_upload
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases
from dmr_utils3.utils import bytes_3, int_id, get_alias
from dmr_utils3 import decode, bptc, const
import config
@@ -1183,8 +1183,6 @@ if __name__ == '__main__':
logger.info('(REPORT) TCP Socket reporting not configured')
# HBlink instance creation
# Run aprs_upload loop
aprs_upload(CONFIG)
logger.info('(GLOBAL) HBlink \'bridge.py\' -- SYSTEM STARTING...')
for system in CONFIG['SYSTEMS']:
if CONFIG['SYSTEMS'][system]['ENABLED']:
+1 -3
View File
@@ -46,7 +46,7 @@ from twisted.protocols.basic import NetstringReceiver
from twisted.internet import reactor, task
# Things we import from the main hblink module
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, config_reports, mk_aliases, acl_check, aprs_upload
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, config_reports, mk_aliases, acl_check
from dmr_utils3.utils import bytes_3, int_id, get_alias
from dmr_utils3 import decode, bptc, const
import config
@@ -281,8 +281,6 @@ if __name__ == '__main__':
report_server = config_reports(CONFIG, reportFactory)
# HBlink instance creation
# Run aprs_upload loop
aprs_upload(CONFIG)
logger.info('HBlink \'bridge_all.py\' -- SYSTEM STARTING...')
for system in CONFIG['SYSTEMS']:
if CONFIG['SYSTEMS'][system]['ENABLED']:
+1 -70
View File
@@ -46,19 +46,6 @@ 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
@@ -97,7 +84,7 @@ REPORT_CLIENTS: 127.0.0.1
# used.
#
[LOGGER]
LOG_FILE: /tmp/hblink.log
LOG_FILE: ./hblink.log
LOG_HANDLERS: console-timed
LOG_LEVEL: DEBUG
LOG_NAME: HBlink
@@ -148,14 +135,6 @@ 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:
@@ -163,51 +142,6 @@ 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
@@ -257,9 +191,6 @@ 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
Regular → Executable
+84 -188
View File
@@ -2,8 +2,6 @@
#
###############################################################################
# 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
@@ -44,7 +42,7 @@ from twisted.protocols.basic import NetstringReceiver
from twisted.internet import reactor, task
# Things we import from the main hblink module
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases, aprs_upload, sendAprs
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases
from dmr_utils3.utils import bytes_3, int_id, get_alias
from dmr_utils3 import decode, bptc, const
import config
@@ -57,7 +55,6 @@ 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
@@ -111,7 +108,7 @@ __email__ = 'n0mjs@me.com'
# AT-D878 - Compressed UDP
# MD-380 - Unified Data Transport
hdr_type = ''
btf = -1
btf = ''
ssid = ''
# From dmr_utils3, modified to decode entire packet. Works for 1/2 rate coded data.
@@ -160,79 +157,31 @@ def aprs_send(packet):
AIS.close()
logger.info('Packet sent to APRS-IS.')
def dashboard_loc_write(call, lat, lon, time, comment):
dash_entries = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read())
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_loc_write(call, lat, lon, time):
pass
## #try:
## 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]))
## 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 ' + bb_file).read())
# except:
# dash_entries = []
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.')
pass
## #try:
## dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').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]))
## user_bb_file.close()
## logger.info('User bulletin entry saved.')
## #logger.info(dash_bb)
# Send email via SMTP function
def send_email(to_email, email_subject, email_message):
@@ -258,38 +207,34 @@ def decdeg2dms(dd):
def user_setting_write(dmr_id, setting, value):
## try:
# Open file and load as dict for modification
with open(user_settings_file, 'r') as f:
with open("./user_settings.txt", '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_file, 'w') as user_dict_file:
with open("./user_settings.txt", '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 = ''
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):
@@ -303,37 +248,19 @@ 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(), re.sub('@BB|@BB ','',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))
elif '@' and ' E-' in sms:
email_message = str(re.sub('.*@|.* E-', '', sms))
to_email = str(re.sub(' E-.*', '', sms))
email_message = re.sub('.*@|.* E-', '', sms)
to_email = 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 Exception as error_exception:
except:
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:
@@ -357,7 +284,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_file).read())
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
if int_id(_rf_src) not in user_settings:
ssid = str(user_ssid)
icon_table = '/'
@@ -378,7 +305,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)
@@ -386,21 +313,19 @@ 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(), comment)
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, strftime('%H:%M:%S - %m/%d/%y'))
#logger.info('Sent manual position to APRS')
except Exception as error_exception:
except:
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_file).read())
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
if int_id(_rf_src) in user_settings and user_settings[int_id(_rf_src)][1]['ssid'] != '':
ssid = user_settings[int_id(_rf_src)][1]['ssid']
else:
@@ -411,19 +336,15 @@ def process_sms(_rf_src, sms):
aprslib.parse(aprs_msg_pkt)
aprs_send(aprs_msg_pkt)
#logger.info('Packet sent.')
except Exception as error_exception:
except:
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 Exception as error_exception:
except:
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
@@ -1527,18 +1448,13 @@ 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)
# 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
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
# 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_file).read())
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
if int_id(_rf_src) not in user_settings:
ssid = str(user_ssid)
icon_table = '/'
@@ -1559,7 +1475,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)
@@ -1570,12 +1486,10 @@ 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, time(), comment)
dashboard_loc_write(str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid, aprs_lat, aprs_lon, strftime('%H:%M:%S - %m/%d/%y'))
#logger.info('Sent APRS packet')
except Exception as error_exception:
except:
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:
@@ -1634,7 +1548,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_file).read())
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
if int_id(_rf_src) not in user_settings:
ssid = str(user_ssid)
icon_table = '/'
@@ -1655,16 +1569,14 @@ class routerHBP(HBSYSTEM):
ssid = user_settings[int_id(_rf_src)][1]['ssid']
if user_settings[int_id(_rf_src)][3]['comment'] != '':
comment = user_settings[int_id(_rf_src)][3]['comment']
aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment)
aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment)
logger.info(aprs_loc_packet)
logger.info('User comment: ' + comment)
logger.info('User SSID: ' + ssid)
logger.info('User icon: ' + icon_table + icon_icon)
except Exception as error_exception:
except:
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))
logger.info(error_exception)
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
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))
try:
# Try parse of APRS packet. If it fails, it will not upload to APRS-IS
aprslib.parse(aprs_loc_packet)
@@ -1672,11 +1584,9 @@ 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), time(), comment)
except Exception as error_exception:
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:
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
@@ -1789,6 +1699,32 @@ 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)')
@@ -1821,45 +1757,6 @@ 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']
# 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_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'
@@ -1917,7 +1814,6 @@ 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)
Regular → Executable
-16
View File
@@ -106,7 +106,6 @@ def build_config(_config_file):
CONFIG['GLOBAL'] = {}
CONFIG['REPORTS'] = {}
CONFIG['LOGGER'] = {}
CONFIG['APRS'] = {}
CONFIG['GPS_DATA'] = {}
CONFIG['ALIASES'] = {}
CONFIG['SYSTEMS'] = {}
@@ -162,11 +161,6 @@ 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']:
@@ -184,15 +178,6 @@ def build_config(_config_file):
'STALE_TIME': config.getint(section, 'STALE_DAYS') * 86400,
})
elif section == 'APRS':
CONFIG['APRS'].update({
'ENABLED': config.getboolean(section, 'ENABLED'),
'CALLSIGN': config.get(section, 'CALLSIGN'),
'REPORT_INTERVAL': config.getint(section, 'REPORT_INTERVAL'),
'SERVER': config.get(section, 'SERVER'),
'MESSAGE': config.get(section, 'MESSAGE')
})
elif config.getboolean(section, 'ENABLED'):
if config.get(section, 'MODE') == 'PEER':
CONFIG['SYSTEMS'].update({section: {
@@ -289,7 +274,6 @@ def build_config(_config_file):
CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'),
'ENABLED': config.getboolean(section, 'ENABLED'),
'APRS': config.getboolean(section, 'APRS'),
'REPEAT': config.getboolean(section, 'REPEAT'),
'MAX_PEERS': config.getint(section, 'MAX_PEERS'),
'IP': gethostbyname(config.get(section, 'IP')),
+2 -49
View File
@@ -84,7 +84,7 @@ REPORT_CLIENTS: 127.0.0.1
# used.
#
[LOGGER]
LOG_FILE: /tmp/gps_data.log
LOG_FILE: ./gps_data.log
LOG_HANDLERS: console-timed
LOG_LEVEL: DEBUG
LOG_NAME: HBlink3 GPS/Data
@@ -105,7 +105,6 @@ 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.
#
@@ -127,11 +126,6 @@ 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.
@@ -141,54 +135,13 @@ 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
http://www.aprs-is.net/javAPRSFilter.aspx# to enable the email gateway. Leave as is to disable.
# 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 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
+84 -199
View File
@@ -48,7 +48,6 @@ 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
@@ -71,12 +70,12 @@ from gps_functions import cmd_list
# Module for maidenhead grids
try:
import maidenhead as mh
except Exception as error_exception:
except:
logger.info('Error importing maidenhead module, make sure it is installed.')
# Module for sending email
try:
import smtplib
except Exception as error_exception:
except:
logger.info('Error importing smtplib module, make sure it is installed.')
#Modules for APRS settings
@@ -93,8 +92,6 @@ __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)
@@ -104,7 +101,7 @@ __status__ = 'pre-alpha'
# AT-D878 - Compressed UDP
# MD-380 - Unified Data Transport
hdr_type = ''
btf = -1
btf = ''
ssid = ''
# From dmr_utils3, modified to decode entire packet. Works for 1/2 rate coded data.
@@ -152,87 +149,32 @@ 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, comment):
dash_entries = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read())
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_loc_write(call, lat, lon, time):
pass
## #try:
## 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]))
## 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 ' + bb_file).read())
# except:
# dash_entries = []
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.')
pass
## #try:
## dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').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]))
## user_bb_file.close()
## logger.info('User bulletin entry saved.')
## #logger.info(dash_bb)
# Send email via SMTP function
def send_email(to_email, email_subject, email_message):
@@ -258,44 +200,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_file, 'r') as f:
with open("./user_settings.txt", '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_file, 'w') as user_dict_file:
with open("./user_settings.txt", '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(_rf_src), subscriber_ids)) + ' - ' + str(int_id(_rf_src)))
logger.info(str(get_alias(int_id(from_id), subscriber_ids)) + ' - ' + str(int_id(from_id)))
elif sms == 'TEST':
logger.info('It works!')
elif '@ICON' in sms:
@@ -304,37 +241,19 @@ 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.time(), re.sub('@BB|@BB ','',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))
elif '@' and ' E-' in sms:
email_message = str(re.sub('.*@|.* E-', '', sms))
to_email = str(re.sub(' E-.*', '', sms))
email_message = re.sub('.*@|.* E-', '', sms)
to_email = 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 Exception as error_exception:
except:
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:
@@ -358,7 +277,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_file).read())
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
if int_id(_rf_src) not in user_settings:
ssid = str(user_ssid)
icon_table = '/'
@@ -379,7 +298,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)
@@ -387,21 +306,19 @@ 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.time(), comment)
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'))
#logger.info('Sent manual position to APRS')
except Exception as error_exception:
except:
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_file).read())
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
if int_id(_rf_src) in user_settings and user_settings[int_id(_rf_src)][1]['ssid'] != '':
ssid = user_settings[int_id(_rf_src)][1]['ssid']
else:
@@ -412,20 +329,16 @@ def process_sms(_rf_src, sms):
aprslib.parse(aprs_msg_pkt)
aprs_send(aprs_msg_pkt)
#logger.info('Packet sent.')
except Exception as error_exception:
except:
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 Exception as error_exception:
except:
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
@@ -483,18 +396,13 @@ 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)
# 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
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
# 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_file).read())
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
if int_id(_rf_src) not in user_settings:
ssid = str(user_ssid)
icon_table = '/'
@@ -515,7 +423,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)
@@ -526,12 +434,10 @@ 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.time(), comment)
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'))
#logger.info('Sent APRS packet')
except Exception as error_exception:
except:
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:
@@ -555,7 +461,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)))
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(ahex(bptc_decode(_data)))
if _seq == 0:
n_packet_assembly = 0
@@ -570,6 +476,7 @@ 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:
@@ -590,7 +497,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_file).read())
user_settings = ast.literal_eval(os.popen('cat ./user_settings.txt').read())
if int_id(_rf_src) not in user_settings:
ssid = str(user_ssid)
icon_table = '/'
@@ -611,16 +518,14 @@ class DATA_SYSTEM(HBSYSTEM):
ssid = user_settings[int_id(_rf_src)][1]['ssid']
if user_settings[int_id(_rf_src)][3]['comment'] != '':
comment = user_settings[int_id(_rf_src)][3]['comment']
aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:@' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment)
aprs_loc_packet = str(get_alias(int_id(_rf_src), subscriber_ids)) + '-' + ssid + '>APHBL3,TCPIP*:/' + str(datetime.datetime.utcnow().strftime("%H%M%Sh")) + str(loc.lat[0:7]) + str(loc.lat_dir) + icon_table + str(loc.lon[0:8]) + str(loc.lon_dir) + icon_icon + str(round(loc.true_course)).zfill(3) + '/' + str(round(loc.spd_over_grnd)).zfill(3) + '/' + str(comment)
logger.info(aprs_loc_packet)
logger.info('User comment: ' + comment)
logger.info('User SSID: ' + ssid)
logger.info('User icon: ' + icon_table + icon_icon)
except Exception as error_exception:
except:
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))
logger.info(error_exception)
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
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))
try:
# Try parse of APRS packet. If it fails, it will not upload to APRS-IS
aprslib.parse(aprs_loc_packet)
@@ -628,11 +533,9 @@ 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.time(), comment)
except Exception as error_exception:
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:
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
@@ -701,7 +604,29 @@ 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)')
@@ -731,46 +656,6 @@ 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 -1
View File
@@ -1,9 +1,11 @@
# Various configurable options used in the GPS/Data application.
# SMS message that triggers command
cmd_list = {
'LS' : 'ls -lsh',
'COMMAND' : 'echo "command to execute"',
'UPTIME' : 'uptime'
}
Regular → Executable
+1 -17
View File
@@ -46,19 +46,6 @@ 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
@@ -97,7 +84,7 @@ REPORT_CLIENTS: 127.0.0.1
# used.
#
[LOGGER]
LOG_FILE: /tmp/hblink.log
LOG_FILE: ./hblink.log
LOG_HANDLERS: console-timed
LOG_LEVEL: DEBUG
LOG_NAME: HBlink
@@ -167,9 +154,6 @@ 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
+18 -191
View File
@@ -27,9 +27,6 @@ works stand-alone before troubleshooting any applications that use it. It has
sufficient logging to be used standalone as a troubleshooting application.
'''
# Added config option for APRS in the master config section. Will only send packets to APRS-IS if each master is enabled.
# Modified by KF7EEL - 10-15-2020
# Specifig functions from modules we need
from binascii import b2a_hex as ahex
from binascii import a2b_hex as bhex
@@ -38,9 +35,6 @@ from hashlib import sha256, sha1
from hmac import new as hmac_new, compare_digest
from time import time
from collections import deque
import aprslib
import os
# Twisted is pretty important, so I keep it separate
from twisted.internet.protocol import DatagramProtocol, Factory, Protocol
@@ -72,124 +66,6 @@ __email__ = 'n0mjs@me.com'
# Global variables used whether we are a module or __main__
systems = {}
open("nom_aprs","w").close
file_config=config.build_config('hblink.cfg')
#CONFIG = ''
def sendAprs():
AIS = aprslib.IS(str(file_config['APRS']['CALLSIGN']), passwd=aprslib.passcode(str(file_config['APRS']['CALLSIGN'])), host=str(file_config['APRS']['SERVER']), port=14580)
AIS.connect()
f = open('nom_aprs', 'r')
lines = f.readlines()
if lines:
for line in lines:
if line != ' ':
lat_verso = ''
lon_verso = ''
dati = line.split(":")
d1_c = int(float(dati[4]))
d2_c = int(float(dati[5]))
if d1_c < 0:
d1 = abs(d1_c)
dm1=abs(float(dati[4])) - d1
dm1_s= float(dm1) * 60
dm1_u="{:.4f}".format(dm1_s)
if int(str(dm1_s).split(".")[0]) < 10:
if d1 < 10 and d1 > -10:
lat_utile='0'+str(d1)+'0'+str(dm1_u)
else:
lat_utile = str(d1)+'0'+str(dm1_u)
else:
if d1 < 10 and d1 > -10:
lat_utile='0'+str(d1)+str(dm1_u)
else:
lat_utile = str(d1)+str(dm1_u)
lat_verso = 'S'
else:
d1 = int(float(dati[4]))
dm1=float(dati[4]) - d1
dm1_s= float(dm1) * 60
dm1_u="{:.4f}".format(dm1_s)
if int(str(dm1_s).split(".")[0]) < 10:
if int(str(dm1_s).split(".")[0]) < 10:
if d1 < 10 and d1 > -10:
lat_utile='0'+str(d1)+'0'+str(dm1_u)
else:
lat_utile = str(d1)+'0'+str(dm1_u)
else:
if d1 < 10 and d1 > -10:
lat_utile='0'+str(d1)+str(dm1_u)
else:
lat_utile = str(d1)+str(dm1_u)
lat_verso = 'N'
if d2_c < 0:
d2=abs(d2_c)
dm2=abs(float(dati[5])) - d2
dm2_s= float(dm2) * 60
dm2_u="{:.3f}".format(dm2_s)
if int(str(dm2_s).split(".")[0]) < 10:
if d2 < 10 and d2 > -10:
lon_utile = '00'+str(d2)+'0'+str(dm2_u)
elif d2 < 100:
lon_utile = '0'+str(d2)+'0'+str(dm2_u)
else:
lon_utile = str(d2)+'0'+str(dm2_s)
else:
if d2 < 10 and d2 > -10:
lon_utile = '00'+str(d2)+str(dm2_u)
elif d2 < 100:
lon_utile = '0'+str(d2)+str(dm2_u)
else:
lon_utile = str(d2)+str(dm2_u)
lon_verso = 'W'
else:
d2=int(float(dati[5]))
dm2=float(dati[5]) - d2
dm2_s= float(dm2) * 60
dm2_u="{:.3f}".format(dm2_s)
if int(str(dm2_s).split(".")[0]) < 10:
if d2 < 10 and d2 > -10:
lon_utile = '00'+str(d2)+'0'+str(dm2_u)
elif d2 < 100:
lon_utile = '0'+str(d2)+'0'+str(dm2_u)
else:
lon_utile = str(d2)+'0'+str(dm2_s)
else:
if d2 < 10 and d2 > -10:
lon_utile = '00'+str(d2)+str(dm2_u)
elif d2 < 100:
lon_utile = '0'+str(d2)+str(dm2_u)
else:
lon_utile = str(d2)+str(dm2_u)
lon_verso = 'E'
rx_utile = dati[2][0:3]+'.'+dati[2][3:]
tx_utile = dati[3][0:3]+'.'+dati[3][3:]
#AIS.sendall(str(dati[0])+">APRS,TCPIP*,qAC,"+str(file_config['APRS']['CALLSIGN'])+":!"+str(lat_utile)[:-2]+lat_verso+"/"+str(lon_utile)[:-1]+lon_verso+"r"+str(file_config['APRS']['MESSAGE'])+' RX: '+str(rx_utile)+' TX: '+str(tx_utile))
AIS.sendall(str(dati[0])+">APRS,TCPIP*,qAC,"+str(file_config['APRS']['CALLSIGN'])+":!"+str(lat_utile)[:7]+lat_verso+"/"+str(lon_utile)[:8]+lon_verso+"r"+str(file_config['APRS']['MESSAGE'])+' RX: '+str(rx_utile)[:8]+' TX: '+str(tx_utile)[:8]) # + ' CC: ' + str(_this_peer['COLORCODE']).decode('UTF-8'))
logging.info('APRS INVIATO/APRS Sent')
def aprs_upload(config):
if config['APRS']['ENABLED']:
if int(config['APRS']['REPORT_INTERVAL']) >= 10:
l=task.LoopingCall(sendAprs)
interval_time = int(int(config['APRS']['REPORT_INTERVAL'])*60)
l.start(interval_time)
else:
l=task.LoopingCall(sendAprs)
l.start(15*60)
logger.info('Report Time APRS to short')
# Timed loop used for reporting HBP status
def config_reports(_config, _factory):
def reporting_loop(_logger, _server):
@@ -204,8 +80,8 @@ def config_reports(_config, _factory):
reporting = task.LoopingCall(reporting_loop, logger, report_server)
reporting.start(_config['REPORTS']['REPORT_INTERVAL'])
return report_server
return report_server
# Shut ourselves down gracefully by disconnecting from the masters and peers.
@@ -228,7 +104,6 @@ def acl_check(_id, _acl):
# OPENBRIDGE CLASS
#************************************************
class OPENBRIDGE(DatagramProtocol):
def __init__(self, _name, _config, _report):
# Define a few shortcuts to make the rest of the class more readable
@@ -237,9 +112,7 @@ class OPENBRIDGE(DatagramProtocol):
self._report = _report
self._config = self._CONFIG['SYSTEMS'][self._system]
self._laststrid = deque([], 20)
def dereg(self):
logger.info('(%s) is mode OPENBRIDGE. No De-Registration required, continuing shutdown', self._system)
@@ -601,19 +474,8 @@ class HBSYSTEM(DatagramProtocol):
and self._peers[_peer_id]['SOCKADDR'] == _sockaddr:
logger.info('(%s) Peer is closing down: %s (%s)', self._system, self._peers[_peer_id]['CALLSIGN'], int_id(_peer_id))
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
if self._CONFIG['SYSTEMS'][self._system]['APRS']:
#if self._config['APRS_ENABLED'] == True:
fn = 'nom_aprs'
f = open(fn)
output = []
for line in f:
if not str(int_id(_peer_id)) in line:
output.append(line)
f.close()
f = open(fn, 'w')
f.writelines(output)
f.close()
del self._peers[_peer_id]
else:
_peer_id = _data[4:8] # Configure Command
if _peer_id in self._peers \
@@ -640,48 +502,6 @@ class HBSYSTEM(DatagramProtocol):
self.send_peer(_peer_id, b''.join([RPTACK, _peer_id]))
logger.info('(%s) Peer %s (%s) has sent repeater configuration', self._system, _this_peer['CALLSIGN'], _this_peer['RADIO_ID'])
#APRS IMPLEMENTATION
conta = 0
lista_blocco=['ysf', 'xlx', 'nxdn', 'dstar', 'echolink','p25', 'svx', 'l1nk']
#if self._CONFIG['SYSTEMS']['APRS_ENABLED']['ENABLED'] and self._CONFIG['APRS']['ENABLED'] and not str(_this_peer['CALLSIGN'].decode('UTF-8')).replace(' ', '').isalpha() :
# Check if master has APRS enabled instead of global.
if self._CONFIG['SYSTEMS'][self._system]['APRS'] and not str(_this_peer['CALLSIGN'].decode('UTF-8')).replace(' ', '').isalpha() :
file = open("nom_aprs","r")
linee = file.readlines()
file.close()
for link in lista_blocco:
if int(str(_this_peer['CALLSIGN'].decode('UTF-8')).replace(' ', '').find(link.upper())) == 0:
conta = conta + 1
if len(linee) > 0:
logging.info('Leggo')
for linea in linee:
dati_l = linea.split(':')
if str(_this_peer['RADIO_ID']) == str(dati_l[1]):
conta = conta + 1
if conta == 0:
file=open("nom_aprs",'a')
if len(str(_this_peer['RADIO_ID'])) > 7:
id_pr=int(str(_this_peer['RADIO_ID'])[-2:])
callsign_u=str(_this_peer['CALLSIGN'].decode('UTF-8'))+"-"+str(id_pr)
file.write(callsign_u.replace(' ', '')+ ":"+ str(_this_peer['RADIO_ID']) +":"+ str(_this_peer['RX_FREQ'].decode('UTF-8')) + ":" + str(_this_peer['TX_FREQ'].decode('UTF-8'))+ ":" + str(_this_peer['LATITUDE'].decode('UTF-8')) + ":" + str(_this_peer['LONGITUDE'].decode('UTF-8')) + "\n")
file.close()
else:
file.write(str(_this_peer['CALLSIGN'].decode('UTF-8')).replace(' ', '')+ ":"+ str(_this_peer['RADIO_ID']) +":"+ str(_this_peer['RX_FREQ'].decode('UTF-8')) + ":" + str(_this_peer['TX_FREQ'].decode('UTF-8'))+ ":" + str(_this_peer['LATITUDE'].decode('UTF-8')) + ":" + str(_this_peer['LONGITUDE'].decode('UTF-8')) + "\n")
file.close()
else:
if conta == 0:
file=open("nom_aprs",'a')
if len(str(_this_peer['RADIO_ID'])) > 7:
id_pr=int(str(_this_peer['RADIO_ID'])[-2:])
callsign_u=str(_this_peer['CALLSIGN'].decode('UTF-8'))+"-"+str(id_pr)
file.write(callsign_u.replace(' ', '')+ ":"+ str(_this_peer['RADIO_ID']) +":"+ str(_this_peer['RX_FREQ'].decode('UTF-8')) + ":" + str(_this_peer['TX_FREQ'].decode('UTF-8'))+ ":" + str(_this_peer['LATITUDE'].decode('UTF-8')) + ":" + str(_this_peer['LONGITUDE'].decode('UTF-8')) + "\n")
file.close()
else:
file.write(str(_this_peer['CALLSIGN'].decode('UTF-8')).replace(' ', '')+ ":"+ str(_this_peer['RADIO_ID']) +":"+ str(_this_peer['RX_FREQ'].decode('UTF-8')) + ":" + str(_this_peer['TX_FREQ'].decode('UTF-8'))+ ":" + str(_this_peer['LATITUDE'].decode('UTF-8')) + ":" + str(_this_peer['LONGITUDE'].decode('UTF-8')) + "\n")
file.close()
else:
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
logger.warning('(%s) Peer info from Radio ID that has not logged in: %s', self._system, int_id(_peer_id))
@@ -699,6 +519,18 @@ class HBSYSTEM(DatagramProtocol):
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
logger.warning('(%s) Ping from Radio ID that is not logged in: %s', self._system, int_id(_peer_id))
elif _command == RPTO:
_peer_id = _data[4:8]
if _peer_id in self._peers \
and self._peers[_peer_id]['CONNECTION'] == 'YES' \
and self._peers[_peer_id]['SOCKADDR'] == _sockaddr:
logger.info('(%s) Peer %s (%s) has send options: %s', self._system, self._peers[_peer_id]['CALLSIGN'], int_id(_peer_id), _data[8:])
self.transport.write(b''.join([RPTACK, _peer_id]), _sockaddr)
elif _command == DMRA:
_peer_id = _data[4:8]
logger.info('(%s) Recieved DMR Talker Alias from peer %s, subscriber %s', self._system, self._peers[_peer_id]['CALLSIGN'], int_id(_rf_src))
else:
logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, ahex(_data))
@@ -828,6 +660,7 @@ class HBSYSTEM(DatagramProtocol):
self._stats['CONNECTION'] = 'YES'
self._stats['CONNECTED'] = time()
logger.info('(%s) Connection to Master Completed', self._system)
# If we are an XLX, send the XLX module request here.
if self._config['MODE'] == 'XLXPEER':
self.send_xlxmaster(self._config['RADIO_ID'], int(4000), self._config['MASTER_SOCKADDR'])
@@ -948,8 +781,6 @@ if __name__ == '__main__':
import sys
import os
import signal
import aprslib
import threading
# Change the current directory to the location of the application
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
@@ -971,12 +802,12 @@ if __name__ == '__main__':
if cli_args.LOG_LEVEL:
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
logger = log.config_logging(CONFIG['LOGGER'])
logger.info('APRS IMPLEMENTATION BY IU7IGU email: iu7igu@yahoo.com \n APRS per master config by KF7EEL - KF7EEL@qsl.net \n\nCopyright (c) 2013, 2014, 2015, 2016, 2018, 2019, 2020\n\tThe Regents of the K0USY Group. All rights reserved.')
logger.info('\n\nCopyright (c) 2013, 2014, 2015, 2016, 2018, 2019, 2020\n\tThe Regents of the K0USY Group. All rights reserved.\n')
logger.debug('(GLOBAL) Logging system started, anything from here on gets logged')
# Set up the signal handler
def sig_handler(_signal, _frame):
logger.info('(GLOBAL) SHUTDOWN: HBLINK IS TERMINATING WITH SIGNAL %s', str(_signal))
logger.info('(GLOBAL) SHUTDOWN: HBLINK IS TERMINATING WITH SIGNAL %s', str(_signal))
hblink_handler(_signal, _frame)
logger.info('(GLOBAL) SHUTDOWN: ALL SYSTEM HANDLERS EXECUTED - STOPPING REACTOR')
reactor.stop()
@@ -995,8 +826,6 @@ if __name__ == '__main__':
logger.info('(REPORT) TCP Socket reporting not configured')
# HBlink instance creation
# Run aprs_upload loop
aprs_upload(CONFIG)
logger.info('(GLOBAL) HBlink \'HBlink.py\' -- SYSTEM STARTING...')
for system in CONFIG['SYSTEMS']:
if CONFIG['SYSTEMS'][system]['ENABLED']:
@@ -1008,5 +837,3 @@ if __name__ == '__main__':
logger.debug('(GLOBAL) %s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system])
reactor.run()
+5
View File
@@ -0,0 +1,5 @@
#! /bin/bash
# Install the required support programs
apt-get install python3 python3-pip -y
pip3 install -r requirements.txt
+2 -2
View File
@@ -3,8 +3,8 @@ bitarray>=0.8.1
Twisted>=16.3.0
dmr_utils3>=0.1.19
configparser>=3.0.0
aprslib>=0.6.42
aprslib
pynmea2
maidenhead
flask
folium
smtplib
-85
View File
@@ -1,85 +0,0 @@
###############################################################################
# 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)
+23 -657
View File
@@ -21,17 +21,9 @@
This is a web dashboard for the GPS/Data application.
'''
from flask import Flask, render_template, request, Response, Markup
from flask import Flask, render_template
import ast, os
#from dashboard_settings import *
import folium
from folium.plugins import MarkerCluster
import re
from datetime import datetime
import argparse
from configparser import ConfigParser
from dashboard_settings import *
app = Flask(__name__)
@@ -46,7 +38,7 @@ tbl_ftr = '''
def get_loc_data():
try:
dash_loc = ast.literal_eval(os.popen('cat ' + loc_file).read())
dash_loc = ast.literal_eval(os.popen('cat /tmp/gps_data_user_loc.txt').read())
tmp_loc = ''
loc_hdr = '''
<tr>
@@ -60,39 +52,25 @@ def get_loc_data():
<h2>&nbsp;<strong>Longitude</strong>&nbsp;</h2>
</td>
<td style="text-align: center;">
<h2>&nbsp;<strong>Time</strong>&nbsp;</h2>
<h2>&nbsp;<strong>Local Time</strong>&nbsp;</h2>
</td>
</tr>
'''
last_known_loc_list = []
display_number = 15
for e in dash_loc:
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>
tmp_loc = tmp_loc + '''<tr>
<td style="text-align: center;"><a href="https://aprs.fi/''' + e['call'] + '''"><strong>''' + e['call'] + '''</strong></a></td>
<td style="text-align: center;"><strong>&nbsp;''' + str(e['lat']) + '''&nbsp;</strong></td>
<td style="text-align: center;"><strong>&nbsp;''' + str(e['lon']) + '''&nbsp;</strong></td>
<td style="text-align: center;">&nbsp;''' + loc_time + '''&nbsp;</td>
<td style="text-align: center;">&nbsp;''' + e['time'] + '''&nbsp;</td>
</tr>'''
return str(str('<h1 style="text-align: center;">Last Known Location</h1>') + tbl_hdr + loc_hdr + tmp_loc + tbl_ftr)
return str(str('<h1 style="text-align: center;">Positions Received</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 ' + bb_file).read())
dash_bb = ast.literal_eval(os.popen('cat /tmp/gps_data_user_bb.txt').read())
tmp_bb = ''
bb_hdr = '''
@@ -101,104 +79,33 @@ def get_bb_data():
<h2><strong>&nbsp;Callsign&nbsp;</strong></h2>
</td>
<td style="text-align: center;">
<h2>&nbsp;<strong>ID</strong>&nbsp; </h2>
<h2>&nbsp;<strong>DMR ID</strong>&nbsp; </h2>
</td>
<td style="text-align: center;">
<h2>&nbsp;<strong>Bulletin</strong>&nbsp;</h2>
</td>
<td style="text-align: center;">
<h2>&nbsp;<strong>Time</strong>&nbsp;</h2>
<h2>&nbsp;<strong>Local Time</strong>&nbsp;</h2>
</td>
</tr>
'''
display_number = 10
for e in dash_bb:
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>&nbsp;''' + e['call'] + '''&nbsp;</strong></td>
<td style="text-align: center;">''' + str(e['dmr_id']) + '''</td>
<td style="text-align: center;"><strong>&nbsp;''' + e['bulletin'] + '''&nbsp;</strong></td>
<td style="text-align: center;">&nbsp;''' + loc_time + '''&nbsp;</td>
</tr>'''
tmp_bb = tmp_bb + '''<tr>
<td style="text-align: center;"><strong>&nbsp;''' + e['call'] + '''&nbsp;</strong></td>
<td style="text-align: center;">''' + str(e['dmr_id']) + '''</td>
<td style="text-align: center;"><strong>&nbsp;''' + e['bulliten'] + '''&nbsp;</strong></td>
<td style="text-align: center;">&nbsp;''' + e['time'] + '''&nbsp;</td>
</tr>'''
return str('<h1 style="text-align: center;">Bulletin Board</h1>' + tbl_hdr + bb_hdr + tmp_bb + tbl_ftr)
except:
return str('<h1 style="text-align: center;">No data</h1>')
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>&nbsp;</p>
<button onclick="window.open('view_map?track=""" + sos_file['call'] + """&reload=30','_blank' );" type="button" class="emergency_button"><h1>View Station on Map</h1></button>
<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, emergency = check_emergency())
return render_template('index.html', title = dashboard_title, logo = logo)
@app.route('/bulletin_board')
def dash_bb():
return get_bb_data()
@@ -206,7 +113,10 @@ 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()
@@ -215,550 +125,6 @@ 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;">&nbsp;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>&nbsp;</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>&nbsp;</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;">&nbsp;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>&nbsp;</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>&nbsp;</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,17 +47,3 @@ 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 -1
View File
@@ -1,5 +1,5 @@
<div>
<hr />
<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>
<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>
</body>
</html>
-5
View File
@@ -1,5 +0,0 @@
{% include 'page.html' %}
{% include 'header.html' %}
{{content}}
{% include 'footer.html' %}
+2 -6
View File
@@ -4,14 +4,10 @@
<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='/';"> 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='/';"> 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='/about';"> Gateway Contact </button></td>
</tr>
</tbody>
</table>
<hr />
+1 -39
View File
@@ -51,11 +51,6 @@
<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>
@@ -74,44 +69,11 @@
<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>
<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>
</tr>
</tbody>
</table>
<p>&nbsp;</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' %}
-12
View File
@@ -16,25 +16,13 @@ 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>
-8
View File
@@ -1,8 +0,0 @@
{% 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;">&nbsp;</p>
<div><p align="center"><iframe style="border: none;" title="Map" src="/view_map?track=" width="1100" height="700"></iframe></p></div>
<p>&nbsp;</p>
{% include 'footer.html' %}