From ac275bee566ba7900c67f3beadc2852e9f4c93a7 Mon Sep 17 00:00:00 2001 From: Dan Srebnick Date: Fri, 15 Sep 2017 07:22:08 -0400 Subject: [PATCH] Publish 0.6 - migration to Python 3 and systemd-journal support --- README | 12 ++- noaacap/DEBIAN/control | 2 +- noaacap/usr/local/bin/noaacap.py | 124 ++++++++++++++-------- noaacap/usr/local/share/noaacap/CHANGELOG | 5 + noaacap/usr/local/share/noaacap/README | 12 ++- packages/noaacap-0.6.deb | Bin 0 -> 6216 bytes 6 files changed, 101 insertions(+), 54 deletions(-) create mode 100644 packages/noaacap-0.6.deb 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 0000000000000000000000000000000000000000..2caeafcdd517281a62dbe8e1a26d73ad6c37f10c GIT binary patch literal 6216 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 nfBpUY&r2041nv`sguKQY+J}PVRx4L7!Hx3;