diff --git a/README b/README index 2eb4d3a..77e0a88 100644 --- a/README +++ b/README @@ -3,13 +3,13 @@ https://github.com/K2DLS/noaacap/wiki/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 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.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. CONFIGURATION @@ -23,10 +23,16 @@ cycle-size 2m beacon via WIDE2-1 \ srccall N0CALL-13 \ + timeout 20 \ exec /usr/local/bin/noaacap.py +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 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. Dan Srebnick, K2DLS -08/19/2017 +09/13/2017 diff --git a/noaacap/DEBIAN/control b/noaacap/DEBIAN/control index 1c4b145..4eebd4c 100644 --- a/noaacap/DEBIAN/control +++ b/noaacap/DEBIAN/control @@ -1,5 +1,5 @@ Package: noaacap -Version: 0.5 +Version: 0.6 Section: hamradio Priority: extra Maintainer: Dan Srebnick diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index 18e504e..84954d5 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 ## Author: Dan Srebnick, K2DLS ## License: BSD-2-Clause (/usr/local/share/noaacap/LICENSE) @@ -6,49 +6,84 @@ ## ## See /usr/local/share/noaacap/CHANGELOG for change history ## -version = "0.5" +version = "0.6" import sys -import pytz, datetime +import pytz +import datetime import string import requests from bs4 import BeautifulSoup -import bsddb -import os -import ConfigParser +from bsddb3 import db +from os import remove +import os.path +import configparser 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': - print "noaacap.py by K2DLS, version " + version - print - print "A weather alert beacon exec for aprx >= 2.9" - print "Licensed under the BSD 2 Clause license" - print "Copyright 2017 by Daniel L. Srebnick" - print + print("noaacap.py by K2DLS, version " + version + "\n") + print("A weather alert beacon exec for aprx >= 2.9") + print("Licensed under the BSD 2 Clause license") + print("Copyright 2017 by Daniel L. Srebnick\n") + sys.exit(0) + +# We need this function early in execution +def Exiting(): + log.info("Exiting") + print() exit(0) conffile = '/etc/noaacap.conf' 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: config.read(conffile) except: - exit("\nError: Check " + conffile + " for proper [noaacap] section heading\n") + log.error("Check " + conffile + " for proper [noaacap] section heading") + Exiting() try: myTZ = config.get('noaacap', 'myTZ') 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: myZone = config.get('noaacap', 'myZone') 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): local = pytz.timezone (TZ) naive = datetime.datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S") @@ -107,22 +142,6 @@ def parsezcs(zcs): 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): @@ -132,10 +151,12 @@ for i in range(0, count): break else: if i == 0: - alerts = bsddb.hashopen(dbfile,'c') - pp = bsddb.hashopen(ppmap,'r') + alerts = db.DB() + 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"): @@ -144,15 +165,16 @@ for i in range(0, count): try: ProductClass, Action, Office, Phenomena, Significance, ETN, \ EventBegin, EventEnd = vtecparse(VTEC['VTEC'].string) - if (ProductClass <> "/O"): #Loop if not operational + if (ProductClass != "/O"): #Loop if not operational continue except: 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[id] == updated: + if alerts[id].decode('utf-8') == updated: + log.debug(id.decode('utf-8') + " " + updated + " found") continue alerts[id] = updated @@ -176,10 +198,10 @@ for i in range(0, count): t = t + chr(s+61) # Pull specific alert to get compressed UGC zones - try: - rs = requests.get(entries[i].id.string) - except: - exit('\n') + rs = requests.get(entries[i].id.string) + if rs.status_code != 200: + log.error(str(rs.status_code) + " " + rs) + Exiting() soup2 = BeautifulSoup(rs.text, 'xml') parms = soup2.find_all('parameter') @@ -188,19 +210,22 @@ for i in range(0, count): if parms[j].valueName.string == 'UGC': zcs = parms[j].value.string - type = pp[str(Phenomena)] - event = sg[Significance] + type = pp[bytes(Phenomena, 'utf-8')].decode('utf-8') if Action == "CAN": event = "CANCL" + else: + event = sg[Significance] hit = 1 message = exputc + "z," + type + "," + zcs + log.info("Msg: " + message) # Make sure message does not exceed 67 chars. If it # does, trim it. Also be certain that myZone is included # included in the trimmed zcs. + n = 0 while len(message) > 67: n = zcs.rfind('-') zcs = zcs[0:n] @@ -209,10 +234,15 @@ for i in range(0, count): zcs = myZone + "-" + zcs message = exputc + "z," + type + "," + zcs + if n > 0: + log.info("Truncated Msg: " + message) + line = "{" + t + "00" - print ":NWS_" + event + ":" + message + line + print(":NWS_" + event + ":" + message + line) break if (hit == 0): - print + print() + +log.info("Exiting") diff --git a/noaacap/usr/local/share/noaacap/CHANGELOG b/noaacap/usr/local/share/noaacap/CHANGELOG index 837b517..d0464f0 100644 --- a/noaacap/usr/local/share/noaacap/CHANGELOG +++ b/noaacap/usr/local/share/noaacap/CHANGELOG @@ -1,5 +1,10 @@ noaacap changelog ## +Version: 0.6 +Release: September 15, 2017 +Converted to Python3 +Added systemd journal support for logging and debugging +## Version: 0.5 Release: August 31, 2017 Bug fix for long messages to ensure myZone is included diff --git a/noaacap/usr/local/share/noaacap/README b/noaacap/usr/local/share/noaacap/README index a69f329..8beec3e 100644 --- a/noaacap/usr/local/share/noaacap/README +++ b/noaacap/usr/local/share/noaacap/README @@ -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 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.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. CONFIGURATION @@ -18,10 +18,16 @@ cycle-size 2m beacon via WIDE2-1 \ srccall N0CALL-13 \ + timeout 20 \ exec /usr/local/bin/noaacap.py +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 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. Dan Srebnick, K2DLS -08/19/2017 +09/13/2017 diff --git a/packages/noaacap-0.6.deb b/packages/noaacap-0.6.deb new file mode 100644 index 0000000..2caeafc Binary files /dev/null and b/packages/noaacap-0.6.deb differ