From df64969adfef8ca2674d5d65df0438f9526112ef Mon Sep 17 00:00:00 2001 From: K2IE Date: Mon, 25 Mar 2024 11:32:32 -0400 Subject: [PATCH 01/10] Update noaacap for CAP 1.2 data source --- noaacap/usr/local/bin/noaacap.py | 128 ++++++++-------------- noaacap/usr/local/share/noaacap/CHANGELOG | 4 + noaacap/usr/local/share/noaacap/LICENSE | 2 +- 3 files changed, 51 insertions(+), 83 deletions(-) diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index b5f73b0..46b8d76 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -11,6 +11,7 @@ version = "1.0" import sys import pytz import datetime +import dateutil.parser import string import requests from bs4 import BeautifulSoup @@ -29,7 +30,7 @@ if len(sys.argv) == 2 and sys.argv[1] == '-v': print("noaacap.py by K2IE, version " + version + "\n") print("A weather alert beacon exec for aprx >= 2.9 and Direwolf >= 1.3") print("Licensed under the BSD 2 Clause license") - print("Copyright 2017-2020 by Daniel L. Srebnick\n") + print("Copyright 2017-2024 by Daniel L. Srebnick\n") sys.exit(0) # We need this function early in execution @@ -95,7 +96,7 @@ except: log.error("or 30 (for 1h if beacon cycle-size 2m)") ErrExit() -url = 'https://alerts.weather.gov/cap/wwaatmget.php?x=' + myZone + '&y=0' +url = 'https://api.weather.gov/alerts/active.atom?zone=' + myZone try: r = requests.get(url, timeout=2) @@ -135,53 +136,10 @@ def vtecparse(value): 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 - hit = 0 for i in range(0, count): - log.debug("Processing entry: " + str(count)) + log.debug("Processing entry: " + str(i + 1)) if ("no active" in entries[i].title.string): if os.path.isfile(dbfile): os.remove(dbfile) @@ -200,26 +158,51 @@ for i in range(0, count): if (entries[i].status.string == "Actual"): + # Retrieve link for entry i + url = entries[i].id.string + ".cap" + + try: + r = requests.get(url, timeout=2) + except requests.exceptions.Timeout: + log.error("Timeout exception requesting " + url) + ErrExit() + + if r.status_code != 200: + log.error(str(r1.status_code) + " " + url) + ErrExit() + + soup = BeautifulSoup(r.text, 'xml') + + for j in soup.select('parameter'): + if (j.find('valueName').text == 'VTEC'): + VTEC = j.find('value').text + + log.debug("VTEC String: " + VTEC) + # 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) + EventBegin, EventEnd = vtecparse(VTEC) except: + log.debug("VTEC parse failed") continue #Loop if error parsing P-VTEC if (ProductClass != "/O"): #Loop if not operational continue # Is alert expired? - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.timezone.utc) # Fix exit on exp = '000000T0000Z' + exp = dateutil.parser.parse(EventEnd) try: - exp = datetime.datetime.strptime(EventEnd,'%y%m%dT%H%MZ') + exp = dateutil.parser.parse(EventEnd) except: exp = now + log.debug('Now: ' + datetime.datetime.strftime(now,"%y-%m-%d %H:%M") + \ ' Exp: ' + datetime.datetime.strftime(exp,"%y-%m-%d %H:%M")) + if now > exp: log.debug("Alert Expired") continue @@ -270,32 +253,22 @@ for i in range(0, count): 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, timeout=2) - except requests.exceptions.Timeout: - log.error("Timeout exception requesting " + url) - ErrExit() - - if rs.status_code != 200: - log.error(str(rs.status_code) + " " + rs) - ErrExit() - - soup2 = BeautifulSoup(rs.text, 'xml') - parms = soup2.find_all('parameter') - j = len(parms) zcs = '' - for j in range(0, len(parms)): - if parms[j].valueName.string == 'UGC': - zcs = parms[j].value.string - - log.debug('Parameters follow\n' + str(parms)) + zcs_discrete = '' + for j in soup.select('area'): + for k in j.select('geocode'): + if (k.find('valueName').text == 'UGC'): + addzcs = k.find('value').text + zcs = zcs + addzcs + '-' + zcs_discrete = zcs_discrete + addzcs + "\n" # This handles messages found to contain empty UGC list if zcs == '': log.debug('No UGC list in message') continue + zcs = zcs[:-1] + log.debug("ZCS Value: " + zcs) type = pp[bytes(Phenomena, 'utf-8')].decode('utf-8') if Action == "CAN": @@ -307,15 +280,6 @@ for i in range(0, count): message = exputc + "z," + type + "," + zcs log.info("Msg: " + message) - # Pull discrete zone UGC values - soup3 = BeautifulSoup(rs.text, 'xml') - parms = soup3.find_all('geocode') - j = len(parms) - zcs_discrete = '' - for j in range(0, len(parms)): - if parms[j].valueName.string == 'UGC': - zcs_discrete = zcs_discrete + parms[j].value.string + "\n" - # Determine if adjacent zones are in original message prior # to any necessary truncation. @@ -339,13 +303,13 @@ for i in range(0, count): n = zcs.rfind('-') zcs = zcs[0:n] message = exputc + "z," + type + "," + zcs - if not myZone in parsezcs(zcs): + if not myZone in zcs: zcs = myZone + "-" + zcs message = exputc + "z," + type + "," + zcs - if adjZone1True and not adjZone1 in parsezcs(zcs): + if adjZone1True and not adjZone1 in zcs: zcs = adjZone1 + "-" + zcs message = exputc + "z," + type + "," + zcs - if adjZone2True and not adjZone2 in parsezcs(zcs): + if adjZone2True and not adjZone2 in zcs: zcs = adjZone2 + "-" + zcs message = exputc + "z," + type + "," + zcs @@ -353,7 +317,7 @@ for i in range(0, count): log.info("Truncated Msg: " + message) line = "{" + t + "00" - print(":NWS_" + event + ":" + message + line) + print(":NWS-" + event + ":" + message + line) if myResend > 0: resend[id] = bytes(str(myResend), 'utf-8') diff --git a/noaacap/usr/local/share/noaacap/CHANGELOG b/noaacap/usr/local/share/noaacap/CHANGELOG index d4599da..69b373f 100644 --- a/noaacap/usr/local/share/noaacap/CHANGELOG +++ b/noaacap/usr/local/share/noaacap/CHANGELOG @@ -1,5 +1,9 @@ noaacap changelog ## +Version: 1.1 +Release: March 25, 2024 +Updated to use CAP 1.2 data source +## Version: 1.0 Release: February 6, 2020 Added ability to specify up to 2 adjacent zones diff --git a/noaacap/usr/local/share/noaacap/LICENSE b/noaacap/usr/local/share/noaacap/LICENSE index 499c68b..7f641c7 100644 --- a/noaacap/usr/local/share/noaacap/LICENSE +++ b/noaacap/usr/local/share/noaacap/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2017-2020, Daniel L. Srebnick +Copyright (c) 2017-2024, Daniel L. Srebnick All rights reserved. Redistribution and use in source and binary forms, with or without From be3c44f63763cf8afc0fee51c5067a508d7c26bc Mon Sep 17 00:00:00 2001 From: K2IE Date: Mon, 25 Mar 2024 12:11:25 -0400 Subject: [PATCH 02/10] Update deb pkg deps and bump version to 1.1 --- noaacap/DEBIAN/control | 4 ++-- noaacap/usr/local/bin/noaacap.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/noaacap/DEBIAN/control b/noaacap/DEBIAN/control index 6bba399..8e66033 100644 --- a/noaacap/DEBIAN/control +++ b/noaacap/DEBIAN/control @@ -1,8 +1,8 @@ Package: noaacap -Version: 1.0.0 +Version: 1.1.0 Section: hamradio Priority: extra Maintainer: Dan Srebnick Architecture: all -Depends: aprx (>=2.9) | direwolf (>=1.3), db-util, python3-lxml, python3-pip, python3-bs4, python3-tz, python3-bsddb3, python3-systemd +Depends: aprx (>=2.9) | direwolf (>=1.3), db-util, python3-lxml, python3-pip, python3-bs4, python3-tz, python3-bsddb3, python3-systemd,python3-dateutil Description: APRS Weather Alerts beacon exec for aprx & Dire Wolf diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index 46b8d76..72e5cfd 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -6,7 +6,7 @@ ## ## See /usr/local/share/noaacap/CHANGELOG for change history ## -version = "1.0" +version = "1.1" import sys import pytz From 64c4b7748e0073d46fa5619c0fa1fcf3c84c4957 Mon Sep 17 00:00:00 2001 From: K2IE Date: Mon, 25 Mar 2024 19:48:13 -0400 Subject: [PATCH 03/10] Bug fixes --- noaacap/usr/local/bin/noaacap.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index 72e5cfd..e9deee1 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -6,7 +6,7 @@ ## ## See /usr/local/share/noaacap/CHANGELOG for change history ## -version = "1.1" +version = "1.1.1" import sys import pytz @@ -173,6 +173,7 @@ for i in range(0, count): soup = BeautifulSoup(r.text, 'xml') + VTEC = '' for j in soup.select('parameter'): if (j.find('valueName').text == 'VTEC'): VTEC = j.find('value').text @@ -194,7 +195,6 @@ for i in range(0, count): # Is alert expired? now = datetime.datetime.now(datetime.timezone.utc) # Fix exit on exp = '000000T0000Z' - exp = dateutil.parser.parse(EventEnd) try: exp = dateutil.parser.parse(EventEnd) except: @@ -212,8 +212,9 @@ for i in range(0, count): id = bytes(str(Office + Phenomena + Significance + ETN), 'utf-8') # Do we have this alert? - if alerts.has_key(id): + if id in alerts: # Is the updated time unchanged? + log.debug('ID found in alerts. Now compare last updated time.') if alerts[id].decode('utf-8') == updated: log.debug(id.decode('utf-8') + " " + updated + " found") # Is resend behavoir desired? @@ -233,7 +234,7 @@ for i in range(0, count): else: continue - alerts[id] = updated + alerts.put(id, updated.encode('utf-8')) effutc = aprstime(entries[i].effective.string[0:-6],myTZ) exputc = aprstime(entries[i].expires.string[0:-6],myTZ) From 03668785ab962b1ecbc72bafae5ff546336fa61d Mon Sep 17 00:00:00 2001 From: K2IE Date: Mon, 25 Mar 2024 23:19:21 -0400 Subject: [PATCH 04/10] Further bugfixes --- noaacap/usr/local/bin/noaacap.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index e9deee1..121239c 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -114,6 +114,13 @@ count = len(entries) log.debug("Entry count: " + str(count)) dbfile = '/dev/shm/noaaconf.db' +if (count == 0): + log.info("Exiting - no events found") + if os.path.isfile(dbfile): + os.remove(dbfile) + print() + exit(0) + ppmap = '/usr/local/share/noaacap/ppmap.db' sg = {"W":"WARN ","A":"WATCH","Y":"ADVIS","S":"STMNT","F":"4CAST", @@ -140,19 +147,14 @@ hit = 0 for i in range(0, count): log.debug("Processing entry: " + str(i + 1)) - if ("no active" in entries[i].title.string): - if os.path.isfile(dbfile): - os.remove(dbfile) - break - else: - if i == 0: - alerts = db.DB() - alerts.open(dbfile, "Alerts", db.DB_HASH, db.DB_CREATE) - if myResend > 0: - resend = db.DB() - resend.open(dbfile, "Resend", db.DB_HASH, db.DB_CREATE) - pp = db.DB() - pp.open(ppmap, None, db.DB_HASH, db.DB_RDONLY) + if i == 0: + alerts = db.DB() + alerts.open(dbfile, "Alerts", db.DB_HASH, db.DB_CREATE) + if myResend > 0: + resend = db.DB() + resend.open(dbfile, "Resend", db.DB_HASH, db.DB_CREATE) + pp = db.DB() + pp.open(ppmap, None, db.DB_HASH, db.DB_RDONLY) updated = entries[i].updated.string @@ -211,6 +213,8 @@ for i in range(0, count): id = bytes(str(Office + Phenomena + Significance + ETN), 'utf-8') + log.debug("ID: " + id.decode('utf-8')) + # Do we have this alert? if id in alerts: # Is the updated time unchanged? From 5f357c5706687581f76907c2a134833090c74b7f Mon Sep 17 00:00:00 2001 From: K2IE Date: Wed, 27 Mar 2024 09:53:46 -0400 Subject: [PATCH 05/10] Reintroduce compressed zones in message --- noaacap/DEBIAN/control | 2 +- noaacap/usr/local/bin/noaacap.py | 150 +++++++++++++++------- noaacap/usr/local/share/noaacap/CHANGELOG | 4 + 3 files changed, 110 insertions(+), 46 deletions(-) diff --git a/noaacap/DEBIAN/control b/noaacap/DEBIAN/control index 8e66033..33f1927 100644 --- a/noaacap/DEBIAN/control +++ b/noaacap/DEBIAN/control @@ -1,5 +1,5 @@ Package: noaacap -Version: 1.1.0 +Version: 1.2.0 Section: hamradio Priority: extra Maintainer: Dan Srebnick diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index 121239c..f23c51c 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -6,7 +6,7 @@ ## ## See /usr/local/share/noaacap/CHANGELOG for change history ## -version = "1.1.1" +version = "1.2" import sys import pytz @@ -111,10 +111,9 @@ if r.status_code != 200: soup = BeautifulSoup(r.text, 'xml') entries = soup.find_all('entry') count = len(entries) -log.debug("Entry count: " + str(count)) dbfile = '/dev/shm/noaaconf.db' -if (count == 0): +if count == 0: log.info("Exiting - no events found") if os.path.isfile(dbfile): os.remove(dbfile) @@ -143,10 +142,16 @@ def vtecparse(value): return ProductClass, Action, Office, Phenomena, Significance, ETN, \ EventBegin, EventEnd +def move_entry(mylist,entry_val,entry_pos): + z_index = mylist.index(entry_val) + z_value = mylist.pop(z_index) + mylist.insert(entry_pos,z_value) + return mylist + hit = 0 for i in range(0, count): - log.debug("Processing entry: " + str(i + 1)) + log.info("Processing entry: " + str(i + 1) + " of " + str(count)) if i == 0: alerts = db.DB() alerts.open(dbfile, "Alerts", db.DB_HASH, db.DB_CREATE) @@ -158,7 +163,7 @@ for i in range(0, count): updated = entries[i].updated.string - if (entries[i].status.string == "Actual"): + if entries[i].status.string == "Actual": # Retrieve link for entry i url = entries[i].id.string + ".cap" @@ -177,7 +182,7 @@ for i in range(0, count): VTEC = '' for j in soup.select('parameter'): - if (j.find('valueName').text == 'VTEC'): + if j.find('valueName').text == 'VTEC': VTEC = j.find('value').text log.debug("VTEC String: " + VTEC) @@ -191,7 +196,7 @@ for i in range(0, count): log.debug("VTEC parse failed") continue #Loop if error parsing P-VTEC - if (ProductClass != "/O"): #Loop if not operational + if ProductClass != "/O": #Loop if not operational continue # Is alert expired? @@ -202,8 +207,8 @@ for i in range(0, count): except: exp = now - log.debug('Now: ' + datetime.datetime.strftime(now,"%y-%m-%d %H:%M") + \ - ' Exp: ' + datetime.datetime.strftime(exp,"%y-%m-%d %H:%M")) +# log.debug('Time Now: ' + datetime.datetime.strftime(now,"%y-%m-%d %H:%M") + '\n' + \ +# 'Expiration: ' + datetime.datetime.strftime(exp,"%y-%m-%d %H:%M")) if now > exp: log.debug("Alert Expired") @@ -213,14 +218,14 @@ for i in range(0, count): id = bytes(str(Office + Phenomena + Significance + ETN), 'utf-8') - log.debug("ID: " + id.decode('utf-8')) +# log.debug("ID: " + id.decode('utf-8')) # Do we have this alert? if id in alerts: # Is the updated time unchanged? - log.debug('ID found in alerts. Now compare last updated time.') +# log.debug('ID found in alerts. Now compare last updated time.') if alerts[id].decode('utf-8') == updated: - log.debug(id.decode('utf-8') + " " + updated + " found") + log.debug(id.decode('utf-8') + " " + updated + " has already been sent") # Is resend behavoir desired? if myResend > 0: @@ -258,20 +263,100 @@ for i in range(0, count): elif 36 <= s <= 61: t = t + chr(s+61) - zcs = '' - zcs_discrete = '' + zcs_discrete = [] for j in soup.select('area'): for k in j.select('geocode'): if (k.find('valueName').text == 'UGC'): addzcs = k.find('value').text - zcs = zcs + addzcs + '-' - zcs_discrete = zcs_discrete + addzcs + "\n" + zcs_discrete.append(addzcs) # This handles messages found to contain empty UGC list - if zcs == '': + if zcs_discrete == []: log.debug('No UGC list in message') continue + zcs = '' + streak = 0 + dupe_flag = 0 + sorted_zcs = (sorted(zcs_discrete)) + + # Move myZone to first position in case of truncation +# z_index = sorted_zcs.index(myZone) +# z_value = sorted_zcs.pop(z_index) +# sorted_zcs.insert(0,z_value) + + move_entry(sorted_zcs,myZone,0) + + # Move adjacent zones, if present, after myZone + + adjZone2Index = 1 + if adjZone1 != '' and adjZone1 in sorted_zcs: + adjZone2Index = 2 + move_entry(sorted_zcs,adjZone1,1) + log.debug("adjZone1 found in sorted zcs and moved to index 1") + + if adjZone2 != '' and adjZone2 in sorted_zcs: + move_entry(sorted_zcs,adjZone2,adjZone2Index) + log.debug("adjZone2 found in sorted zcs and moved to index " + str(adjZone2Index)) + + k = 0 + for j in sorted_zcs[:-1]: + k += 1 + curr_item = j + if (k == 1): + zcs = curr_item + next_item = sorted_zcs[k] + curr_prefix = curr_item[0:3] + next_prefix = next_item[0:3] + curr_suffix = curr_item[3:6] + next_suffix = next_item[3:6] + i_curr_suffix = int(curr_suffix) + i_next_suffix = int(next_suffix) + i_curr_plus1 = i_curr_suffix + 1 + + # Prefix differs + if next_prefix != curr_prefix: + # If not on streak, print next_item + if streak == 0: + zcs = zcs + '-' + next_item + # Otherwise the streak is over and is printed, along with the next item + else: + streak = 0 + streak_end = curr_suffix + zcs = zcs + '-' + streak_start + '>' + streak_end + "-" + next_item + elif i_next_suffix != i_curr_plus1: + # If not on a streak set dupe flag in case a streak is next + if streak == 0: + zcs = zcs + '-' + next_suffix + dupe_flag = 1 + else: + # The streak is over + streak = 0 + streak_end = curr_suffix + # The beginning of the streak was not previously printer + if dupe_flag == 0: + zcs = zcs + '-' + streak_start + '>' + streak_end + # Don't print the start of the streak as it is a dupe + else: + zcs = zcs + '>' + streak_end + dupe_flag = 0 + elif i_next_suffix == i_curr_plus1: + # Set flag when new streak detected + if streak == 0: + streak = 1 + streak_start = curr_suffix + streak_end = next_suffix + # The streak continues + else: + streak_end == next_suffix + else: + log.error("Unexpected condition during zcs compression") + ErrExit() + + if streak == 1: + streak_end = next_suffix + zcs = zcs + '>' + streak_end + zcs = zcs[:-1] log.debug("ZCS Value: " + zcs) type = pp[bytes(Phenomena, 'utf-8')].decode('utf-8') @@ -285,50 +370,25 @@ for i in range(0, count): message = exputc + "z," + type + "," + zcs log.info("Msg: " + message) - # Determine if adjacent zones are in original message prior - # to any necessary truncation. - - adjZone1True = 0 - adjZone2True = 0 - - if adjZone1 != '' and adjZone1 in zcs_discrete: - adjZone1True = 1 - log.debug("adjZone1 found in alert data") - - if adjZone2 != '' and adjZone2 in zcs_discrete: - adjZone2True = 1 - log.debug("adjZone2 found in alert data") - - # 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. + # Make sure message does not exceed 67 chars. If it does, trim it. n = 0 while len(message) > 67: n = zcs.rfind('-') zcs = zcs[0:n] message = exputc + "z," + type + "," + zcs - if not myZone in zcs: - zcs = myZone + "-" + zcs - message = exputc + "z," + type + "," + zcs - if adjZone1True and not adjZone1 in zcs: - zcs = adjZone1 + "-" + zcs - message = exputc + "z," + type + "," + zcs - if adjZone2True and not adjZone2 in zcs: - zcs = adjZone2 + "-" + 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) if myResend > 0: resend[id] = bytes(str(myResend), 'utf-8') break -if (hit == 0): +if hit == 0: print() log.info("Exiting") diff --git a/noaacap/usr/local/share/noaacap/CHANGELOG b/noaacap/usr/local/share/noaacap/CHANGELOG index 69b373f..61fbeca 100644 --- a/noaacap/usr/local/share/noaacap/CHANGELOG +++ b/noaacap/usr/local/share/noaacap/CHANGELOG @@ -1,5 +1,9 @@ noaacap changelog ## +Version: 1.2 +Release: March 27, 2024 +Reintroduced compressed zones and other improvements +## Version: 1.1 Release: March 25, 2024 Updated to use CAP 1.2 data source From a1eb77aa3d628fa2b77e7120d0eaaa99eef11e24 Mon Sep 17 00:00:00 2001 From: K2IE Date: Wed, 27 Mar 2024 10:45:48 -0400 Subject: [PATCH 06/10] Remove unneeded comments in .py --- noaacap/usr/local/bin/noaacap.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index f23c51c..4b2badd 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -281,14 +281,9 @@ for i in range(0, count): sorted_zcs = (sorted(zcs_discrete)) # Move myZone to first position in case of truncation -# z_index = sorted_zcs.index(myZone) -# z_value = sorted_zcs.pop(z_index) -# sorted_zcs.insert(0,z_value) - move_entry(sorted_zcs,myZone,0) # Move adjacent zones, if present, after myZone - adjZone2Index = 1 if adjZone1 != '' and adjZone1 in sorted_zcs: adjZone2Index = 2 From 2e3470c41f76a10c9a897f57862da1769d3834aa Mon Sep 17 00:00:00 2001 From: K2IE Date: Wed, 27 Mar 2024 16:32:10 -0400 Subject: [PATCH 07/10] v1.3 fixes truncation bug, recommends zone vs county, updated README --- noaacap/DEBIAN/control | 2 +- noaacap/usr/local/bin/noaacap.py | 3 +-- noaacap/usr/local/share/noaacap/CHANGELOG | 4 ++++ noaacap/usr/local/share/noaacap/README | 15 ++++++++++----- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/noaacap/DEBIAN/control b/noaacap/DEBIAN/control index 33f1927..1c3d149 100644 --- a/noaacap/DEBIAN/control +++ b/noaacap/DEBIAN/control @@ -1,5 +1,5 @@ Package: noaacap -Version: 1.2.0 +Version: 1.3.0 Section: hamradio Priority: extra Maintainer: Dan Srebnick diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index 4b2badd..b7c22c6 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -6,7 +6,7 @@ ## ## See /usr/local/share/noaacap/CHANGELOG for change history ## -version = "1.2" +version = "1.3" import sys import pytz @@ -352,7 +352,6 @@ for i in range(0, count): streak_end = next_suffix zcs = zcs + '>' + streak_end - zcs = zcs[:-1] log.debug("ZCS Value: " + zcs) type = pp[bytes(Phenomena, 'utf-8')].decode('utf-8') diff --git a/noaacap/usr/local/share/noaacap/CHANGELOG b/noaacap/usr/local/share/noaacap/CHANGELOG index 61fbeca..a1fba3d 100644 --- a/noaacap/usr/local/share/noaacap/CHANGELOG +++ b/noaacap/usr/local/share/noaacap/CHANGELOG @@ -1,5 +1,9 @@ noaacap changelog ## +Version: 1.3 +Release: TBD +Fixed truncation bug in compressed zone list +## Version: 1.2 Release: March 27, 2024 Reintroduced compressed zones and other improvements diff --git a/noaacap/usr/local/share/noaacap/README b/noaacap/usr/local/share/noaacap/README index 4b54062..1440ddb 100644 --- a/noaacap/usr/local/share/noaacap/README +++ b/noaacap/usr/local/share/noaacap/README @@ -14,7 +14,7 @@ https://github.com/K2IE/noaacap/releases Once you are running aprx >= 2.9 or direwolf >= 1.3, install noaacap: -Install via sudo dpkg -i noaacap-1.0.deb. Then, to satisfy the dependencies, +Install via sudo dpkg -i noaacap-1.3.deb. Then, to satisfy the dependencies, run sudo apt-get -f install. ==> APRX CONFIGURATION <== @@ -57,10 +57,14 @@ 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 your county code. A list of county codes may be found -at https://alerts.weather.gov/. Only list one county. The program will then -send the alert for all affected zones. You could instead use a zone code if -you want to be more specific. +myZone should be set to your zone code. A list of zone codes may be found +at https://www.weather.gov/pimar/PubZone. Only list one zone. The program +will send the alert for all affected zones that will fit in the allocated 67 +characters. You could instead use a county code if you wish. However the +associated zone may also be sent in, wasting characters. + +As an example, if you're located in Lancaster, PA, the map shows that you +are in zone 066. Enter PA066 for myZone. myResend should be set to 0 for no message resends or to an interval of your choosing. If noaacap beacons every 2 minutes, a value of 30 would provide @@ -82,3 +86,4 @@ weather alerts via this program. Dan Srebnick, K2IE 02/06/2020 +Updated 03/27/2024 From 421aca6699e2b7ed66c90bb2eac7735598951343 Mon Sep 17 00:00:00 2001 From: K2IE Date: Sun, 31 Mar 2024 10:32:22 -0400 Subject: [PATCH 08/10] Fix README typo --- noaacap/usr/local/share/noaacap/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noaacap/usr/local/share/noaacap/README b/noaacap/usr/local/share/noaacap/README index 1440ddb..09e5bcb 100644 --- a/noaacap/usr/local/share/noaacap/README +++ b/noaacap/usr/local/share/noaacap/README @@ -64,7 +64,7 @@ characters. You could instead use a county code if you wish. However the associated zone may also be sent in, wasting characters. As an example, if you're located in Lancaster, PA, the map shows that you -are in zone 066. Enter PA066 for myZone. +are in zone 066. Enter PAZ066 for myZone. myResend should be set to 0 for no message resends or to an interval of your choosing. If noaacap beacons every 2 minutes, a value of 30 would provide From 2680769fcd6ade04bff70aeceaf90bf989c5bbd6 Mon Sep 17 00:00:00 2001 From: K2IE Date: Tue, 2 Apr 2024 13:19:42 -0400 Subject: [PATCH 09/10] Fix expiration date parsing issue --- noaacap/DEBIAN/control | 2 +- noaacap/usr/local/bin/noaacap.py | 10 ++++++---- noaacap/usr/local/share/noaacap/CHANGELOG | 4 ++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/noaacap/DEBIAN/control b/noaacap/DEBIAN/control index 1c3d149..af9833f 100644 --- a/noaacap/DEBIAN/control +++ b/noaacap/DEBIAN/control @@ -1,5 +1,5 @@ Package: noaacap -Version: 1.3.0 +Version: 1.3.1 Section: hamradio Priority: extra Maintainer: Dan Srebnick diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index b7c22c6..7ae19b7 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -6,7 +6,7 @@ ## ## See /usr/local/share/noaacap/CHANGELOG for change history ## -version = "1.3" +version = "1.3.1" import sys import pytz @@ -199,16 +199,18 @@ for i in range(0, count): if ProductClass != "/O": #Loop if not operational continue + EventEnd = "20" + EventEnd + # Is alert expired? - now = datetime.datetime.now(datetime.timezone.utc) + now = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0) # Fix exit on exp = '000000T0000Z' try: exp = dateutil.parser.parse(EventEnd) except: exp = now -# log.debug('Time Now: ' + datetime.datetime.strftime(now,"%y-%m-%d %H:%M") + '\n' + \ -# 'Expiration: ' + datetime.datetime.strftime(exp,"%y-%m-%d %H:%M")) +# log.debug('Time Now: ' + datetime.datetime.strftime(now,"%y-%m-%d %H:%M")) +# log.debug('Expiration: ' + datetime.datetime.strftime(exp,"%y-%m-%d %H:%M")) if now > exp: log.debug("Alert Expired") diff --git a/noaacap/usr/local/share/noaacap/CHANGELOG b/noaacap/usr/local/share/noaacap/CHANGELOG index a1fba3d..4de785d 100644 --- a/noaacap/usr/local/share/noaacap/CHANGELOG +++ b/noaacap/usr/local/share/noaacap/CHANGELOG @@ -1,5 +1,9 @@ noaacap changelog ## +Version: 1.3.1 +Release: April 2, 2024 +Fix expiration date parsing issue +## Version: 1.3 Release: TBD Fixed truncation bug in compressed zone list From 4f1e7e49d255209416df61f7d5f64f4ff4f1a23f Mon Sep 17 00:00:00 2001 From: K2IE Date: Wed, 3 Apr 2024 10:27:51 -0400 Subject: [PATCH 10/10] v1.4 Complete rewrite of zone parsing --- noaacap/DEBIAN/control | 2 +- noaacap/etc/noaacap.conf | 6 +- noaacap/usr/local/bin/noaacap.py | 88 ++++++++++++-------------- noaacap/usr/local/share/noaacap/README | 9 ++- 4 files changed, 50 insertions(+), 55 deletions(-) diff --git a/noaacap/DEBIAN/control b/noaacap/DEBIAN/control index af9833f..fad6076 100644 --- a/noaacap/DEBIAN/control +++ b/noaacap/DEBIAN/control @@ -1,5 +1,5 @@ Package: noaacap -Version: 1.3.1 +Version: 1.4.0 Section: hamradio Priority: extra Maintainer: Dan Srebnick diff --git a/noaacap/etc/noaacap.conf b/noaacap/etc/noaacap.conf index 8be8a1f..d719a54 100644 --- a/noaacap/etc/noaacap.conf +++ b/noaacap/etc/noaacap.conf @@ -1,7 +1,7 @@ [noaacap] myTZ = America/New_York -myZone = NJC025 +myZone = NJZ012 myResend = 30 -adjZone1 = NJZ012 -adjZone2 = NJZ020 +adjZone1 = NJZ013 +adjZone2 = NJZ014 Logging = 2 diff --git a/noaacap/usr/local/bin/noaacap.py b/noaacap/usr/local/bin/noaacap.py index 7ae19b7..95658c1 100755 --- a/noaacap/usr/local/bin/noaacap.py +++ b/noaacap/usr/local/bin/noaacap.py @@ -6,7 +6,7 @@ ## ## See /usr/local/share/noaacap/CHANGELOG for change history ## -version = "1.3.1" +version = "1.4.0" import sys import pytz @@ -278,8 +278,6 @@ for i in range(0, count): continue zcs = '' - streak = 0 - dupe_flag = 0 sorted_zcs = (sorted(zcs_discrete)) # Move myZone to first position in case of truncation @@ -296,63 +294,57 @@ for i in range(0, count): move_entry(sorted_zcs,adjZone2,adjZone2Index) log.debug("adjZone2 found in sorted zcs and moved to index " + str(adjZone2Index)) + prev_item = '' + prev_prefix = '' + prev_suffix = '' + next_item = '' +# next_prefix = '' + next_suffix = '' + k = 0 - for j in sorted_zcs[:-1]: + for j in sorted_zcs: k += 1 curr_item = j - if (k == 1): - zcs = curr_item - next_item = sorted_zcs[k] + + try: + next_item = sorted_zcs[k] + except: + next_item = '' + if next_item != '': +# next_prefix = next_item[0:3] + next_suffix = next_item[3:6] + i_next_suffix = int(next_suffix) + curr_prefix = curr_item[0:3] - next_prefix = next_item[0:3] curr_suffix = curr_item[3:6] - next_suffix = next_item[3:6] i_curr_suffix = int(curr_suffix) - i_next_suffix = int(next_suffix) i_curr_plus1 = i_curr_suffix + 1 - # Prefix differs - if next_prefix != curr_prefix: - # If not on streak, print next_item - if streak == 0: - zcs = zcs + '-' + next_item - # Otherwise the streak is over and is printed, along with the next item - else: - streak = 0 - streak_end = curr_suffix - zcs = zcs + '-' + streak_start + '>' + streak_end + "-" + next_item - elif i_next_suffix != i_curr_plus1: - # If not on a streak set dupe flag in case a streak is next - if streak == 0: - zcs = zcs + '-' + next_suffix - dupe_flag = 1 - else: - # The streak is over - streak = 0 - streak_end = curr_suffix - # The beginning of the streak was not previously printer - if dupe_flag == 0: - zcs = zcs + '-' + streak_start + '>' + streak_end - # Don't print the start of the streak as it is a dupe - else: - zcs = zcs + '>' + streak_end - dupe_flag = 0 - elif i_next_suffix == i_curr_plus1: - # Set flag when new streak detected - if streak == 0: - streak = 1 - streak_start = curr_suffix - streak_end = next_suffix - # The streak continues - else: - streak_end == next_suffix + # Always print entire first item + if k == 1: + zcs = curr_item + # Did prefix change? + elif prev_prefix != curr_prefix: + zcs = zcs + "-" + curr_item + # Current suffix is 1 more than previous + elif i_prev_plus1 == i_curr_suffix: + if i_next_suffix != i_curr_plus1: + zcs = zcs + ">" + curr_suffix + # Current suffix is not 1 more than previous + elif i_prev_plus1 != i_curr_suffix: + zcs = zcs + "-" + curr_suffix else: log.error("Unexpected condition during zcs compression") ErrExit() - if streak == 1: - streak_end = next_suffix - zcs = zcs + '>' + streak_end + # For debugging only + # print (zcs) + + prev_item = curr_item + prev_prefix = curr_prefix + prev_suffix = curr_suffix + i_prev_suffix = int(prev_suffix) + i_prev_plus1 = i_prev_suffix + 1 log.debug("ZCS Value: " + zcs) type = pp[bytes(Phenomena, 'utf-8')].decode('utf-8') diff --git a/noaacap/usr/local/share/noaacap/README b/noaacap/usr/local/share/noaacap/README index 09e5bcb..d1e5d2e 100644 --- a/noaacap/usr/local/share/noaacap/README +++ b/noaacap/usr/local/share/noaacap/README @@ -1,3 +1,6 @@ +The current release of noaacap is 1.4.0. If you are running older than this +version, please update before asking for support. + This software is written in python3 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 of aprx may be found at http://thelifeofkenneth.com/aprx/debs/. @@ -14,7 +17,7 @@ https://github.com/K2IE/noaacap/releases Once you are running aprx >= 2.9 or direwolf >= 1.3, install noaacap: -Install via sudo dpkg -i noaacap-1.3.deb. Then, to satisfy the dependencies, +Install via sudo dpkg -i noaacap-1.4.deb. Then, to satisfy the dependencies, run sudo apt-get -f install. ==> APRX CONFIGURATION <== @@ -61,7 +64,7 @@ myZone should be set to your zone code. A list of zone codes may be found at https://www.weather.gov/pimar/PubZone. Only list one zone. The program will send the alert for all affected zones that will fit in the allocated 67 characters. You could instead use a county code if you wish. However the -associated zone may also be sent in, wasting characters. +associated zone may also be sent, wasting characters. As an example, if you're located in Lancaster, PA, the map shows that you are in zone 066. Enter PAZ066 for myZone. @@ -86,4 +89,4 @@ weather alerts via this program. Dan Srebnick, K2IE 02/06/2020 -Updated 03/27/2024 +Updated 04/03/2024