Publish 0.6 - migration to Python 3 and systemd-journal support
This commit is contained in:
parent
616f3f2b5e
commit
ac275bee56
12
README
12
README
|
|
@ -3,13 +3,13 @@ https://github.com/K2DLS/noaacap/wiki/noaacap
|
||||||
|
|
||||||
ABOUT NOAACAP
|
ABOUT NOAACAP
|
||||||
|
|
||||||
This software is written in python 2.7 and has been tested with aprx 2.9.x.
|
This software is written in python 3.4 and has been tested with aprx 2.9.x.
|
||||||
If you are running a version < 2.9.0 you should first update aprx. The current
|
If you are running a version < 2.9.0 you should first update aprx. The current
|
||||||
release may be found at http://thelifeofkenneth.com/aprx/debs/.
|
release may be found at http://thelifeofkenneth.com/aprx/debs/.
|
||||||
|
|
||||||
Once you are running aprx >= 2.9, install noaacap:
|
Once you are running aprx >= 2.9, install noaacap:
|
||||||
|
|
||||||
Install via sudo dpkg -i noaacap-0.5.deb. Then, to satisfy the dependencies,
|
Install via sudo dpkg -i noaacap-0.6.deb. Then, to satisfy the dependencies,
|
||||||
run sudo apt-get -f install.
|
run sudo apt-get -f install.
|
||||||
|
|
||||||
CONFIGURATION
|
CONFIGURATION
|
||||||
|
|
@ -23,10 +23,16 @@ cycle-size 2m
|
||||||
|
|
||||||
beacon via WIDE2-1 \
|
beacon via WIDE2-1 \
|
||||||
srccall N0CALL-13 \
|
srccall N0CALL-13 \
|
||||||
|
timeout 20 \
|
||||||
exec /usr/local/bin/noaacap.py
|
exec /usr/local/bin/noaacap.py
|
||||||
|
|
||||||
</beacon>
|
</beacon>
|
||||||
|
|
||||||
|
The timeout was needed on my older and slower single core Raspberry Pi Model B.
|
||||||
|
The aprx default timeout is 10 seconds and the timeout may not be needed on a
|
||||||
|
RPi 2 or newer. If you need lots of "BEACON EXEC abnormal close" messages in
|
||||||
|
aprx.log, then you likely need to add the timeout.
|
||||||
|
|
||||||
The program will check for a new alert or update every 2 minutes, but only
|
The program will check for a new alert or update every 2 minutes, but only
|
||||||
send if there is a change.
|
send if there is a change.
|
||||||
|
|
||||||
|
|
@ -50,4 +56,4 @@ k2dls@k2dls.net with the callsign-SSID of your APRS station that is sending
|
||||||
weather alerts via this program.
|
weather alerts via this program.
|
||||||
|
|
||||||
Dan Srebnick, K2DLS
|
Dan Srebnick, K2DLS
|
||||||
08/19/2017
|
09/13/2017
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Package: noaacap
|
Package: noaacap
|
||||||
Version: 0.5
|
Version: 0.6
|
||||||
Section: hamradio
|
Section: hamradio
|
||||||
Priority: extra
|
Priority: extra
|
||||||
Maintainer: Dan Srebnick <k2dls at k2dls.net>
|
Maintainer: Dan Srebnick <k2dls at k2dls.net>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python3
|
||||||
|
|
||||||
## Author: Dan Srebnick, K2DLS
|
## Author: Dan Srebnick, K2DLS
|
||||||
## License: BSD-2-Clause (/usr/local/share/noaacap/LICENSE)
|
## License: BSD-2-Clause (/usr/local/share/noaacap/LICENSE)
|
||||||
|
|
@ -6,49 +6,84 @@
|
||||||
##
|
##
|
||||||
## See /usr/local/share/noaacap/CHANGELOG for change history
|
## See /usr/local/share/noaacap/CHANGELOG for change history
|
||||||
##
|
##
|
||||||
version = "0.5"
|
version = "0.6"
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import pytz, datetime
|
import pytz
|
||||||
|
import datetime
|
||||||
import string
|
import string
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
import bsddb
|
from bsddb3 import db
|
||||||
import os
|
from os import remove
|
||||||
import ConfigParser
|
import os.path
|
||||||
|
import configparser
|
||||||
import re
|
import re
|
||||||
|
import logging
|
||||||
|
from systemd.journal import JournalHandler
|
||||||
|
|
||||||
|
log = logging.getLogger('noaacap')
|
||||||
|
log.addHandler(JournalHandler(SYSLOG_IDENTIFIER='noaacap'))
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
log.info("Starting")
|
||||||
|
|
||||||
if len(sys.argv) == 2 and sys.argv[1] == '-v':
|
if len(sys.argv) == 2 and sys.argv[1] == '-v':
|
||||||
print "noaacap.py by K2DLS, version " + version
|
print("noaacap.py by K2DLS, version " + version + "\n")
|
||||||
print
|
print("A weather alert beacon exec for aprx >= 2.9")
|
||||||
print "A weather alert beacon exec for aprx >= 2.9"
|
print("Licensed under the BSD 2 Clause license")
|
||||||
print "Licensed under the BSD 2 Clause license"
|
print("Copyright 2017 by Daniel L. Srebnick\n")
|
||||||
print "Copyright 2017 by Daniel L. Srebnick"
|
sys.exit(0)
|
||||||
print
|
|
||||||
|
# We need this function early in execution
|
||||||
|
def Exiting():
|
||||||
|
log.info("Exiting")
|
||||||
|
print()
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
conffile = '/etc/noaacap.conf'
|
conffile = '/etc/noaacap.conf'
|
||||||
|
|
||||||
if not os.path.isfile(conffile):
|
if not os.path.isfile(conffile):
|
||||||
exit("\nError: " + conffile + " not found\n")
|
log.error(conffile + " not found")
|
||||||
|
Exiting()
|
||||||
|
|
||||||
config = ConfigParser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config.read(conffile)
|
config.read(conffile)
|
||||||
except:
|
except:
|
||||||
exit("\nError: Check " + conffile + " for proper [noaacap] section heading\n")
|
log.error("Check " + conffile + " for proper [noaacap] section heading")
|
||||||
|
Exiting()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
myTZ = config.get('noaacap', 'myTZ')
|
myTZ = config.get('noaacap', 'myTZ')
|
||||||
except:
|
except:
|
||||||
exit("\nError: Check " + conffile + " for proper myTZ value in [noaacap] section\n")
|
log.error("Check " + conffile + " for proper myTZ value in [noaacap] section")
|
||||||
|
Exiting()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
myZone = config.get('noaacap', 'myZone')
|
myZone = config.get('noaacap', 'myZone')
|
||||||
except:
|
except:
|
||||||
exit("\nError: Check " + conffile + " for proper myZone value in [noaacap] section\n")
|
log.error("Check " + conffile + " for proper myZone value in [noaacap] section")
|
||||||
|
Exiting()
|
||||||
|
|
||||||
|
url = 'https://alerts.weather.gov/cap/wwaatmget.php?x=' + myZone + '&y=0'
|
||||||
|
|
||||||
|
r = requests.get(url)
|
||||||
|
if r.status_code != 200:
|
||||||
|
log.error(str(r.status_code) + " " + url)
|
||||||
|
Exiting()
|
||||||
|
|
||||||
|
soup = BeautifulSoup(r.text, 'xml')
|
||||||
|
entries = soup.find_all('entry')
|
||||||
|
count = len(entries)
|
||||||
|
|
||||||
|
dbfile = '/dev/shm/noaacap.db'
|
||||||
|
ppmap = '/usr/local/share/noaacap/ppmap.db'
|
||||||
|
|
||||||
|
sg = {"W":"WARN ","A":"WATCH","Y":"ADVIS","S":"STMNT","F":"4CAST",
|
||||||
|
"O":"OUTLK","N":"SYNOP"}
|
||||||
|
|
||||||
|
# Define functions used in the for loop
|
||||||
def aprstime(timestr,TZ):
|
def aprstime(timestr,TZ):
|
||||||
local = pytz.timezone (TZ)
|
local = pytz.timezone (TZ)
|
||||||
naive = datetime.datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S")
|
naive = datetime.datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S")
|
||||||
|
|
@ -107,22 +142,6 @@ def parsezcs(zcs):
|
||||||
zonelist.add(prefix + zone)
|
zonelist.add(prefix + zone)
|
||||||
return zonelist
|
return zonelist
|
||||||
|
|
||||||
sg = {"W":"WARN ","A":"WATCH","Y":"ADVIS","S":"STMNT","F":"4CAST",
|
|
||||||
"O":"OUTLK","N":"SYNOP"}
|
|
||||||
|
|
||||||
url = 'https://alerts.weather.gov/cap/wwaatmget.php?x=' + myZone + '&y=0'
|
|
||||||
try:
|
|
||||||
r = requests.get(url)
|
|
||||||
except:
|
|
||||||
exit('\n')
|
|
||||||
|
|
||||||
soup = BeautifulSoup(r.text, 'xml')
|
|
||||||
entries = soup.find_all('entry')
|
|
||||||
count = len(entries)
|
|
||||||
|
|
||||||
dbfile = '/dev/shm/noaacap.db'
|
|
||||||
ppmap = '/usr/local/share/noaacap/ppmap.db'
|
|
||||||
|
|
||||||
hit = 0
|
hit = 0
|
||||||
for i in range(0, count):
|
for i in range(0, count):
|
||||||
|
|
||||||
|
|
@ -132,10 +151,12 @@ for i in range(0, count):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if i == 0:
|
if i == 0:
|
||||||
alerts = bsddb.hashopen(dbfile,'c')
|
alerts = db.DB()
|
||||||
pp = bsddb.hashopen(ppmap,'r')
|
alerts.open(dbfile, None, db.DB_HASH, db.DB_CREATE)
|
||||||
|
pp = db.DB()
|
||||||
|
pp.open(ppmap, None, db.DB_HASH, db.DB_RDONLY)
|
||||||
|
|
||||||
updated = str(entries[i].updated.string)
|
updated = entries[i].updated.string
|
||||||
|
|
||||||
if (entries[i].status.string == "Actual"):
|
if (entries[i].status.string == "Actual"):
|
||||||
|
|
||||||
|
|
@ -144,15 +165,16 @@ for i in range(0, count):
|
||||||
try:
|
try:
|
||||||
ProductClass, Action, Office, Phenomena, Significance, ETN, \
|
ProductClass, Action, Office, Phenomena, Significance, ETN, \
|
||||||
EventBegin, EventEnd = vtecparse(VTEC['VTEC'].string)
|
EventBegin, EventEnd = vtecparse(VTEC['VTEC'].string)
|
||||||
if (ProductClass <> "/O"): #Loop if not operational
|
if (ProductClass != "/O"): #Loop if not operational
|
||||||
continue
|
continue
|
||||||
except:
|
except:
|
||||||
continue #Loop if error parsing P-VTEC
|
continue #Loop if error parsing P-VTEC
|
||||||
|
|
||||||
id = str(Office + Phenomena + Significance + ETN)
|
id = bytes(str(Office + Phenomena + Significance + ETN), 'utf-8')
|
||||||
|
|
||||||
if alerts.has_key(id):
|
if alerts.has_key(id):
|
||||||
if alerts[id] == updated:
|
if alerts[id].decode('utf-8') == updated:
|
||||||
|
log.debug(id.decode('utf-8') + " " + updated + " found")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
alerts[id] = updated
|
alerts[id] = updated
|
||||||
|
|
@ -176,10 +198,10 @@ for i in range(0, count):
|
||||||
t = t + chr(s+61)
|
t = t + chr(s+61)
|
||||||
|
|
||||||
# Pull specific alert to get compressed UGC zones
|
# Pull specific alert to get compressed UGC zones
|
||||||
try:
|
rs = requests.get(entries[i].id.string)
|
||||||
rs = requests.get(entries[i].id.string)
|
if rs.status_code != 200:
|
||||||
except:
|
log.error(str(rs.status_code) + " " + rs)
|
||||||
exit('\n')
|
Exiting()
|
||||||
|
|
||||||
soup2 = BeautifulSoup(rs.text, 'xml')
|
soup2 = BeautifulSoup(rs.text, 'xml')
|
||||||
parms = soup2.find_all('parameter')
|
parms = soup2.find_all('parameter')
|
||||||
|
|
@ -188,19 +210,22 @@ for i in range(0, count):
|
||||||
if parms[j].valueName.string == 'UGC':
|
if parms[j].valueName.string == 'UGC':
|
||||||
zcs = parms[j].value.string
|
zcs = parms[j].value.string
|
||||||
|
|
||||||
type = pp[str(Phenomena)]
|
type = pp[bytes(Phenomena, 'utf-8')].decode('utf-8')
|
||||||
event = sg[Significance]
|
|
||||||
|
|
||||||
if Action == "CAN":
|
if Action == "CAN":
|
||||||
event = "CANCL"
|
event = "CANCL"
|
||||||
|
else:
|
||||||
|
event = sg[Significance]
|
||||||
|
|
||||||
hit = 1
|
hit = 1
|
||||||
message = exputc + "z," + type + "," + zcs
|
message = exputc + "z," + type + "," + zcs
|
||||||
|
log.info("Msg: " + message)
|
||||||
|
|
||||||
# Make sure message does not exceed 67 chars. If it
|
# Make sure message does not exceed 67 chars. If it
|
||||||
# does, trim it. Also be certain that myZone is included
|
# does, trim it. Also be certain that myZone is included
|
||||||
# included in the trimmed zcs.
|
# included in the trimmed zcs.
|
||||||
|
|
||||||
|
n = 0
|
||||||
while len(message) > 67:
|
while len(message) > 67:
|
||||||
n = zcs.rfind('-')
|
n = zcs.rfind('-')
|
||||||
zcs = zcs[0:n]
|
zcs = zcs[0:n]
|
||||||
|
|
@ -209,10 +234,15 @@ for i in range(0, count):
|
||||||
zcs = myZone + "-" + zcs
|
zcs = myZone + "-" + zcs
|
||||||
message = exputc + "z," + type + "," + zcs
|
message = exputc + "z," + type + "," + zcs
|
||||||
|
|
||||||
|
if n > 0:
|
||||||
|
log.info("Truncated Msg: " + message)
|
||||||
|
|
||||||
line = "{" + t + "00"
|
line = "{" + t + "00"
|
||||||
print ":NWS_" + event + ":" + message + line
|
print(":NWS_" + event + ":" + message + line)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
if (hit == 0):
|
if (hit == 0):
|
||||||
print
|
print()
|
||||||
|
|
||||||
|
log.info("Exiting")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
noaacap changelog
|
noaacap changelog
|
||||||
##
|
##
|
||||||
|
Version: 0.6
|
||||||
|
Release: September 15, 2017
|
||||||
|
Converted to Python3
|
||||||
|
Added systemd journal support for logging and debugging
|
||||||
|
##
|
||||||
Version: 0.5
|
Version: 0.5
|
||||||
Release: August 31, 2017
|
Release: August 31, 2017
|
||||||
Bug fix for long messages to ensure myZone is included
|
Bug fix for long messages to ensure myZone is included
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
This software is written in python 2.7 and has been tested with aprx 2.9.x.
|
This software is written in python 3.4 and has been tested with aprx 2.9.x.
|
||||||
If you are running a version < 2.9.0 you should first update aprx. The current
|
If you are running a version < 2.9.0 you should first update aprx. The current
|
||||||
release may be found at http://thelifeofkenneth.com/aprx/debs/.
|
release may be found at http://thelifeofkenneth.com/aprx/debs/.
|
||||||
|
|
||||||
Once you are running aprx >= 2.9, install noaacap:
|
Once you are running aprx >= 2.9, install noaacap:
|
||||||
|
|
||||||
Install via sudo dpkg -i noaacap-0.5.deb. Then, to satisfy the dependencies,
|
Install via sudo dpkg -i noaacap-0.6.deb. Then, to satisfy the dependencies,
|
||||||
run sudo apt-get -f install.
|
run sudo apt-get -f install.
|
||||||
|
|
||||||
CONFIGURATION
|
CONFIGURATION
|
||||||
|
|
@ -18,10 +18,16 @@ cycle-size 2m
|
||||||
|
|
||||||
beacon via WIDE2-1 \
|
beacon via WIDE2-1 \
|
||||||
srccall N0CALL-13 \
|
srccall N0CALL-13 \
|
||||||
|
timeout 20 \
|
||||||
exec /usr/local/bin/noaacap.py
|
exec /usr/local/bin/noaacap.py
|
||||||
|
|
||||||
</beacon>
|
</beacon>
|
||||||
|
|
||||||
|
The timeout was needed on my older and slower single core Raspberry Pi Model B.
|
||||||
|
The aprx default timeout is 10 seconds and the timeout may not be needed on a
|
||||||
|
RPi 2 or newer. If you need lots of "BEACON EXEC abnormal close" messages in
|
||||||
|
aprx.log, then you likely need to add the timeout.
|
||||||
|
|
||||||
The program will check for a new alert or update every 2 minutes, but only
|
The program will check for a new alert or update every 2 minutes, but only
|
||||||
send if there is a change.
|
send if there is a change.
|
||||||
|
|
||||||
|
|
@ -45,4 +51,4 @@ k2dls@k2dls.net with the callsign-SSID of your APRS station that is sending
|
||||||
weather alerts via this program.
|
weather alerts via this program.
|
||||||
|
|
||||||
Dan Srebnick, K2DLS
|
Dan Srebnick, K2DLS
|
||||||
08/19/2017
|
09/13/2017
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue