Initial commit to git

This commit is contained in:
Dan Srebnick 2017-08-24 21:30:26 -04:00
commit ad0f28b1bb
8 changed files with 283 additions and 0 deletions

1
DEBIAN/conffiles Normal file
View File

@ -0,0 +1 @@
/etc/noaacap.conf

8
DEBIAN/control Normal file
View File

@ -0,0 +1,8 @@
Package: noaacap
Version: 0.4
Section: hamradio
Priority: extra
Maintainer: Dan Srebnick <k2dls at k2dls.net>
Architecture: all
Depends: aprx (>=2.9), db-util, python-lxml, python-pip, python-bs4, python-tz
Description: APRS Weather Alerts beacon exec for aprx

3
DEBIAN/copyright Normal file
View File

@ -0,0 +1,3 @@
Copyright: 2017 Daniel L. Srebnick <k2dls@k2dls.net>
License: BSD-2-clause
Files: *

3
etc/noaacap.conf Normal file
View File

@ -0,0 +1,3 @@
[noaacap]
myTZ = America/New_York
myZone = NJZ014

212
usr/local/bin/noaacap.py Executable file
View File

@ -0,0 +1,212 @@
#!/usr/bin/python
## Author: Dan Srebnick, K2DLS
##
## Version: 0.4
## Release: August 24, 2017
## Switched to compressed zcs to improve capacity
## Improved messsage + \n length check
##
## Version: 0.3
## Release: August 22, 2017
## Changes: Implement NWS-CANCL messages, implement DB id consisting of
## Office + Phenomena + Significance + ETN
##
## Version: 0.2
## Release: August 19, 2017
## Changes: Make sure message + \n does not exceed 67 chars
##
## Version: 0.1
## Release: August 7, 2017
##
## License: BSD-2-Clause (/usr/local/share/noaacap/license.txt)
##
## This program is called from aprx.
##
import pytz, datetime
import string
import requests
from bs4 import BeautifulSoup
import bsddb
import os
import ConfigParser
import re
config = ConfigParser.ConfigParser()
try:
os.path.isfile('/etc/noaacap.conf')
except:
exit("\nError: /etc/noaacap.conf not found\n")
else:
config.read('/etc/noaacap.conf')
myTZ = config.get('noaacap', 'myTZ')
myZone = config.get('noaacap', 'myZone')
def aprstime(timestr,TZ):
local = pytz.timezone (TZ)
naive = datetime.datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S")
local_dt = local.localize(naive, is_dst=None)
utc_dt = local_dt.astimezone (pytz.utc)
return utc_dt.strftime ("%d%H%M")
def vtecparse(value):
line = value.split("\n")[0]
ProductClass, Action, Office, Phenomena, Significance, ETN, DTGroup = \
line.split('.')
EventBegin, EventEnd = DTGroup.split('-')
return ProductClass, Action, Office, Phenomena, Significance, ETN, \
EventBegin, EventEnd
def capchildren(children):
k = None
list = {}
for tag in children:
if tag.name == 'valueName':
k = tag.string
elif tag.name == 'value':
list[k] = tag.string
return list
def parsezcs(zcs):
zonelist = set()
inprogress = 0
newprefix = 1
for z in range(0, len(zcs)):
if not inprogress:
zone = ''
inprogress = 1
if re.match('[A-Z]',zcs[z]):
if newprefix == 1:
prefix = ''
newprefix = 0
prefix = prefix + zcs[z]
if re.match('[0-9]',zcs[z]):
zone = zone + zcs[z]
if zcs[z] == '>':
zonelist.add(prefix + zone)
inprogress = 0
endzone = zcs[z+1:z+4]
z = z + 3
startzone = int(zone) + 1
endzone = int(endzone)
for y in range(startzone, endzone):
zonelist.add(prefix + str(y).zfill(3))
elif zcs[z] == '-':
zonelist.add(prefix + zone)
inprogress = 0
if re.match('[A-Z]',zcs[z+1]):
newprefix = 1
if inprogress:
zonelist.add(prefix + zone)
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
for i in range(0, count):
if ("no active" in entries[i].title.string):
if os.path.isfile(dbfile):
os.remove(dbfile)
break
else:
if i == 0:
alerts = bsddb.hashopen(dbfile,'c')
pp = bsddb.hashopen(ppmap,'r')
updated = str(entries[i].updated.string)
if (entries[i].status.string == "Actual"):
# Parse P-VTEC string and make sure this is an operational ProductClass
VTEC = capchildren(entries[i].parameter.children)
try:
ProductClass, Action, Office, Phenomena, Significance, ETN, \
EventBegin, EventEnd = vtecparse(VTEC['VTEC'].string)
if (ProductClass <> "/O"): #Loop if not operational
continue
except:
continue #Loop if error parsing P-VTEC
id = str(Office + Phenomena + Significance + ETN)
if alerts.has_key(id):
if alerts[id] == updated:
continue
alerts[id] = updated
effutc = aprstime(entries[i].effective.string[0:-6],myTZ)
exputc = aprstime(entries[i].expires.string[0:-6],myTZ)
# Compress effective time to 3 byte
dd = int(effutc[0:2])
hh = int(effutc[2:4])
mm = int(effutc[4:6])
t = ''
for s in (dd, hh, mm):
if 0 <= s <= 9:
t = t + chr(s+48)
elif 10 <= s <= 35:
t = t + chr(s+55)
elif 36 <= s <= 61:
t = t + chr(s+61)
# Pull specific alert to get compressed UGC zones
try:
rs = requests.get(entries[i].id.string)
except:
exit('\n')
soup2 = BeautifulSoup(rs.text, 'xml')
parms = soup2.find_all('parameter')
j = len(parms)
for j in range(0, len(parms)):
if parms[j].valueName.string == 'UGC':
zcs = parms[j].value.string
type = pp[str(Phenomena)]
event = sg[Significance]
if Action == "CAN":
event = "CANCL"
hit = 1
suffix = exputc + "z," + type + "," + zcs + "{" + t + "00"
# Make sure message does not exceed 67 chars (66 + newline)
# If it does, trim it. Also be certain that myZone
# is included in the trimmed zcs.
while len(suffix) > 66:
if not myZone in parsezcs(zcs):
zcs = myZone + "-" + zcs
suffix = exputc + "z," + type + "," + zcs + "{" + t + "00"
n = zcs.rfind('-')
zcs = zcs[0:n]
suffix = exputc + "z," + type + "," + zcs + "{" + t + "00"
print ":NWS_" + event + ":" + suffix
break
if (hit == 0):
print

View File

@ -0,0 +1,47 @@
This software is written in python 2.7 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
release may be found at http://thelifeofkenneth.com/aprx/debs/.
Once you are running aprx >= 2.9, install noaacap:
Install via sudo dpkg -i noaacap-0.1.1.deb. Then, to satisfy the dependencies,
run sudo apt-get -f install.
CONFIGURATION
Add a beacon section similar to the following to /etc/aprx.conf:
<beacon>
beaconmode both
cycle-size 2m
beacon via WIDE2-1 \
srccall N0CALL-13 \
exec /usr/local/bin/noaacap.py
</beacon>
The program will check for a new alert or update every 2 minutes, but only
send if there is a change.
Edit /etc/noaacap.conf:
Do not remove the [noaacap] section header.
myTZ should be set to the Linux TZ value for the issuing office of your
preferred weather zone. For a complete list of timezones, you may use
the following command: timedatectl list-timezones | grep America.
myZone should be set to the zone number. A list of zones may be found at
https://alerts.weather.gov/. Only list one zone. You could use a county code
rather than a zone code if you wish.
USE
It would be good to hear from users of this software. Please email
k2dls@k2dls.net with the callsign-SSID of your APRS station that is sending
weather alerts via this program.
Dan Srebnick, K2DLS
08/19/2017

View File

@ -0,0 +1,9 @@
Copyright 2017 Daniel L. Srebnick (K2DLS)
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.