Publish 0.6 - migration to Python 3 and systemd-journal support

This commit is contained in:
Dan Srebnick 2017-09-15 07:22:08 -04:00
parent 616f3f2b5e
commit ac275bee56
6 changed files with 101 additions and 54 deletions

12
README
View File

@ -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

View File

@ -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>

View File

@ -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")

View File

@ -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

View File

@ -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

BIN
packages/noaacap-0.6.deb Normal file

Binary file not shown.