From ad0f28b1bb6a72a067235f13456d84aa4a831b98 Mon Sep 17 00:00:00 2001 From: Dan Srebnick Date: Thu, 24 Aug 2017 21:30:26 -0400 Subject: [PATCH] Initial commit to git --- DEBIAN/conffiles | 1 + DEBIAN/control | 8 ++ DEBIAN/copyright | 3 + etc/noaacap.conf | 3 + usr/local/bin/noaacap.py | 212 ++++++++++++++++++++++++++++ usr/local/share/noaacap/README | 47 ++++++ usr/local/share/noaacap/license.txt | 9 ++ usr/local/share/noaacap/ppmap.db | Bin 0 -> 12288 bytes 8 files changed, 283 insertions(+) create mode 100644 DEBIAN/conffiles create mode 100644 DEBIAN/control create mode 100644 DEBIAN/copyright create mode 100644 etc/noaacap.conf create mode 100755 usr/local/bin/noaacap.py create mode 100644 usr/local/share/noaacap/README create mode 100644 usr/local/share/noaacap/license.txt create mode 100644 usr/local/share/noaacap/ppmap.db diff --git a/DEBIAN/conffiles b/DEBIAN/conffiles new file mode 100644 index 0000000..b81610f --- /dev/null +++ b/DEBIAN/conffiles @@ -0,0 +1 @@ +/etc/noaacap.conf diff --git a/DEBIAN/control b/DEBIAN/control new file mode 100644 index 0000000..999219c --- /dev/null +++ b/DEBIAN/control @@ -0,0 +1,8 @@ +Package: noaacap +Version: 0.4 +Section: hamradio +Priority: extra +Maintainer: Dan Srebnick +Architecture: all +Depends: aprx (>=2.9), db-util, python-lxml, python-pip, python-bs4, python-tz +Description: APRS Weather Alerts beacon exec for aprx diff --git a/DEBIAN/copyright b/DEBIAN/copyright new file mode 100644 index 0000000..450efac --- /dev/null +++ b/DEBIAN/copyright @@ -0,0 +1,3 @@ +Copyright: 2017 Daniel L. Srebnick +License: BSD-2-clause +Files: * diff --git a/etc/noaacap.conf b/etc/noaacap.conf new file mode 100644 index 0000000..daeb100 --- /dev/null +++ b/etc/noaacap.conf @@ -0,0 +1,3 @@ +[noaacap] +myTZ = America/New_York +myZone = NJZ014 diff --git a/usr/local/bin/noaacap.py b/usr/local/bin/noaacap.py new file mode 100755 index 0000000..e3b73b2 --- /dev/null +++ b/usr/local/bin/noaacap.py @@ -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 diff --git a/usr/local/share/noaacap/README b/usr/local/share/noaacap/README new file mode 100644 index 0000000..970551c --- /dev/null +++ b/usr/local/share/noaacap/README @@ -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: + + + +beaconmode both +cycle-size 2m + +beacon via WIDE2-1 \ + srccall N0CALL-13 \ + exec /usr/local/bin/noaacap.py + + + +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 diff --git a/usr/local/share/noaacap/license.txt b/usr/local/share/noaacap/license.txt new file mode 100644 index 0000000..9d0bbb0 --- /dev/null +++ b/usr/local/share/noaacap/license.txt @@ -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. diff --git a/usr/local/share/noaacap/ppmap.db b/usr/local/share/noaacap/ppmap.db new file mode 100644 index 0000000000000000000000000000000000000000..2fee906399dc89b7177cff8a97765c976b203d03 GIT binary patch literal 12288 zcmeI1O{iN%6vyv*3sGp!QBedp9mPc-6a*3cT$r!>ZXnOLnZ#=&7RO|gd&$k6I5&A; zepGa!C@z#@aiu7@5JjOBx)h|)g-}%dKtT{h5k;ZkMksaW-sClXZd|#L8Mv9RbI+Xo zCY%31p67XB9^1r?S9#ai!L{bO+73K#rRc8s+;7)UdEWI;?|bd;XNoPGW0A+M+-IK8 zUWtzFUAKF#IRcJ=Bj5-)0*-(q;0QPZj({WJ2si?cz<*C*uSTzUKa{-EMf#n7rSIuG z`kcqrcBWN974#^8{shO#-A~durR~9f9I!@&(taDh; zhp=9TM$Z(OLuznSfbO##wuGPyfx!tu8v=8rp_EGID{bO)mVsZ~f&{;7MkO`P7H{)>O;!{MCB6JFlvw5A~`k^l=?$HhvRoK2(x;$8v{C7b;TpqWL{Q zC4h3QO~qoEifOD;D0BNWvig7dfBrkY1g-x6lP=KD^do&mU(h)^OK0dby=xqRlg0sf zo}Q(QCN!dmj?p7@gdU_SJz$joLAsUp6VX0ep-cFiaR9!-FY!Enif8aNet_@c+xRA) z#1nWNpF(qzYmR^;;0QPZj({WJ2si?cfFs}tI0FAO0+*c!KG9;ELHDSY$s5~+Y_=Xn zz5$)X(B7Uz$|%(KQD|qgRE%S2b7(7_ioSyOA>eXDYRTHsEbU93?-$QuIP>Jv9#(#Qy7OKm*vecpJb6TnG5;dL@10!0?k83aD6b4y(zPX z1vKjrZ1=TF)G+QrV7`&HI0}_-{EmhChoA#%B9X9WNT}t6Y3( literal 0 HcmV?d00001