Skip to content

Commit

Permalink
Release v4.1: Add nwsHeadline to alerts.
Browse files Browse the repository at this point in the history
  • Loading branch information
chaunceygardiner committed Jun 27, 2024
1 parent 42d6f92 commit 57fae83
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 21 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Copyright (C)2020-2024 by John A Kline (john@johnkline.com)

**This plugin requires Python 3.7, WeeWX 4 or 5**

**If you are updating from versions less than 4.0, you MUST delete the nws database (nws.sdb) before
**If you are updating from versions less than 4.1, you MUST delete the nws database (nws.sdb) before
restarting weewx. This is because the database schema has changed. If you don't do this, nws
won't work and the following statement will be in the weewx log:
ERROR user.nws: You must delete the nws.sdb database and restart weewx. It contains an old schema!
Expand All @@ -31,11 +31,11 @@ Copyright (C)2020-2024 by John A Kline (john@johnkline.com)
`pip install requests`

1. Download the release from the [github](https://github.com/chaunceygardiner/weewx-nws).
Click on releases and pick the latest release (Release v4.0).
Click on releases and pick the latest release (Release v4.1).

1. Install the nws extension.

`weectl extension install weewx-nws-4.0.zip`
`weectl extension install weewx-nws-4.1.zip`

## WeeWX 4 Installation Instructions

Expand All @@ -52,11 +52,11 @@ Copyright (C)2020-2024 by John A Kline (john@johnkline.com)
```

1. Download the release from the [github](https://github.com/chaunceygardiner/weewx-nws).
Click on releases and pick the latest release (Release v4.0).
Click on releases and pick the latest release (Release v4.1).

1. Run the following command.
```
sudo /home/weewx/bin/wee_extension --install weewx-nws-4.0.zip
sudo /home/weewx/bin/wee_extension --install weewx-nws-4.1.zip
```
Note: The above command assumes a WeeWX installation of `/home/weewx`.
Adjust the command as necessary.
Expand Down Expand Up @@ -323,6 +323,7 @@ Copyright (C)2020-2024 by John A Kline (john@johnkline.com)
$alert.ends # Time it will end
$alert.event # Name of event (e.g., Heat Advisory)
$alert.headline # Headline
$alert.nwsHeadline # NWSheadline
$alert.description # Long description
$alert.instructions # Instructions on what to do
$alert.latitude # Latitude of point for which alerts were requested
Expand All @@ -347,6 +348,7 @@ Copyright (C)2020-2024 by John A Kline (john@johnkline.com)
ends : 06-Sep-2022 20:00
event : Heat Advisory
headline : Heat Advisory issued September 4 at 4:11AM PDT until September 6 at 8:00PM PDT by NWS San Francisco CA
nwsHeadline : HEAT ADVISORY IN EFFECT FROM 4 AM SUNDAY TO 8 PM PDT TUESDAY
description : * WHAT...Temperatures up to 98 expected.<br/>* WHERE...Marin Coastal Range...
instructions: Drink plenty of fluids, stay in an air-conditioned room, stay out of the sun...
latitude : 37.431495
Expand All @@ -373,7 +375,7 @@ Copyright (C)2020-2024 by John A Kline (john@johnkline.com)
#set $alert_count = 0
#for $alert in $nwsforecast.alerts()
#set $alert_count += 1
<tr style='width:100%;'><td style='text-align:center;font-size:$title_font_size;font-weight:bold;border-bottom:2pt solid Black;'>$alert.headline</td></tr>
<tr style='width:100%;'><td style='text-align:center;font-size:$title_font_size;font-weight:bold;border-bottom:2pt solid Black;'>$alert.nwsHeadline</td></tr>
<tr><td style='text-align:left;'><br/>Status: $alert.status</td></tr>
<tr><td style='text-align:left;'>Severity: $alert.severity</td></tr>
<tr><td style='text-align:left;'>Certainty: $alert.certainty</td></tr>
Expand Down
33 changes: 27 additions & 6 deletions bin/user/nws.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

log = logging.getLogger(__name__)

WEEWX_NWS_VERSION = "4.0"
WEEWX_NWS_VERSION = "4.1"

if sys.version_info[0] < 3:
raise weewx.UnsupportedFeature(
Expand Down Expand Up @@ -96,7 +96,8 @@
('certainty', 'STRING'), # For alerts, holds the certainty of the alert, null for others
('urgency', 'STRING'), # For alerts, holds the urgency of the alert, null for others
('sender', 'STRING'), # For alerts, holds the sender of the alert, null for others
('senderName', 'STRING'), # For alerts, holds the senderName of the alert, null for others
('senderName', 'STRING'), # For alerts, holds the senderName of the alert, null for others
('nwsHeadline', 'STRING'), # For alerts, holds the NWSheadline, null for others (and sometimes null for alerts (e.g. test alerts)
]

schema = {
Expand Down Expand Up @@ -143,6 +144,7 @@ class Forecast:
urgency : Optional[str] # For alerts only
sender : Optional[str] # For alerts only
senderName : Optional[str] # For alerts only
nwsHeadline : Optional[str] # For alerts only

@dataclass
class SshConfiguration:
Expand Down Expand Up @@ -548,6 +550,7 @@ def convert_to_json(record, ts) -> Dict[str, Any]:
j['urgency'] = record.urgency
j['sender'] = record.sender
j['senderName'] = record.senderName
j['nwsHeadline'] = record.nwsHeadline
log.debug('convert_to_json: returning: %s' % j)
return j

Expand Down Expand Up @@ -1101,7 +1104,15 @@ def compose_alert_records(j, latitude: str, longitude: str) -> Iterator[Forecast
urgency = alert['urgency'],
sender = alert['sender'],
senderName = alert['senderName'],
nwsHeadline = None
)
if 'NWSheadline' in alert['parameters']:
combinedHeadline = ''
for headline in alert['parameters']['NWSheadline']:
if combinedHeadline != '':
headline += ', '
combinedHeadline += headline
record.nwsHeadline = combinedHeadline
alertCount += 1
log.debug('compose_alert_records: yielding record %s' % record)
yield record
Expand Down Expand Up @@ -1174,7 +1185,8 @@ def compose_records(j, forecast_type: ForecastType, latitude: str, longitude: st
certainty = None,
urgency = None,
sender = None,
senderName = None)
senderName = None,
nwsHeadline = None)
# Work around NWS breakage. Until NWS patches the problem, iconUrl might not be a full URL.
if not record.iconUrl.startswith('http'):
record.iconUrl = 'https://api.weather.gov/' + record.iconUrl
Expand Down Expand Up @@ -1267,6 +1279,7 @@ def alerts(self) -> List[Dict[str, Any]]:
row['urgency'] = raw_row['urgency']
row['sender'] = raw_row['sender']
row['senderName'] = raw_row['senderName']
row['nwsHeadline'] = raw_row['nwsHeadline']
rows.append(row)
return rows

Expand Down Expand Up @@ -1361,7 +1374,7 @@ def fetch_records_internal(dbm: weewx.manager.Manager, forecast_type: ForecastTy
else:
time_select_phrase = "generatedTime = (SELECT MAX(generatedTime)"
order_by_clause = "ORDER BY startTime"
select = "SELECT dateTime, interval, latitude, longitude, usUnits, generatedTime, number, name, startTime, expirationTime, id, endTime, isDaytime, outTemp, outTempTrend, pop, dewpoint, outHumidity, windSpeed, windSpeed2, windDir, iconUrl, shortForecast, detailedForecast, instruction, sent, status, messageType, category, severity, certainty, urgency, sender, senderName FROM archive WHERE %s FROM archive WHERE interval = %d AND latitude = %s AND longitude = %s) AND interval = %d AND latitude = %s AND longitude = %s %s" % (time_select_phrase, NWS.get_interval(forecast_type), latitude, longitude, NWS.get_interval(forecast_type), latitude, longitude, order_by_clause)
select = "SELECT dateTime, interval, latitude, longitude, usUnits, generatedTime, number, name, startTime, expirationTime, id, endTime, isDaytime, outTemp, outTempTrend, pop, dewpoint, outHumidity, windSpeed, windSpeed2, windDir, iconUrl, shortForecast, detailedForecast, instruction, sent, status, messageType, category, severity, certainty, urgency, sender, senderName, nwsHeadline FROM archive WHERE %s FROM archive WHERE interval = %d AND latitude = %s AND longitude = %s) AND interval = %d AND latitude = %s AND longitude = %s %s" % (time_select_phrase, NWS.get_interval(forecast_type), latitude, longitude, NWS.get_interval(forecast_type), latitude, longitude, order_by_clause)
records = []
forecast_count = 0
for row in dbm.genSql(select):
Expand Down Expand Up @@ -1407,6 +1420,7 @@ def fetch_records_internal(dbm: weewx.manager.Manager, forecast_type: ForecastTy
record['urgency'] = row[31]
record['sender'] = row[32]
record['senderName'] = row[33]
record['nwsHeadline'] = row[34]

records.append(record)
return records
Expand Down Expand Up @@ -1758,9 +1772,9 @@ def view_sqlite_database(dbfile: str, forecast_type: ForecastType, criterion: Cr

def print_sqlite_records(conn, dbfile: str, forecast_type: ForecastType, criterion: Criterion) -> None:
if criterion == Criterion.ALL:
select = "SELECT dateTime, interval, latitude, longitude, usUnits, generatedTime, number, name, startTime, expirationTime, id, endTime, isDaytime, outTemp, outTempTrend, pop, dewpoint, outHumidity, windSpeed, windSpeed2, windDir, iconUrl, shortForecast, detailedForecast, instruction, sent, status, messageType, category, severity, certainty, urgency, sender, senderName FROM archive WHERE interval = %d ORDER BY generatedTime, number" % NWS.get_interval(forecast_type)
select = "SELECT dateTime, interval, latitude, longitude, usUnits, generatedTime, number, name, startTime, expirationTime, id, endTime, isDaytime, outTemp, outTempTrend, pop, dewpoint, outHumidity, windSpeed, windSpeed2, windDir, iconUrl, shortForecast, detailedForecast, instruction, sent, status, messageType, category, severity, certainty, urgency, sender, senderName, nwsHeadline FROM archive WHERE interval = %d ORDER BY generatedTime, number" % NWS.get_interval(forecast_type)
elif criterion == Criterion.LATEST:
select = "SELECT dateTime, interval, latitude, longitude, usUnits, generatedTime, number, name, startTime, expirationTime, id, endTime, isDaytime, outTemp, outTempTrend, pop, dewpoint, outHumidity, windSpeed, windSpeed2, windDir, iconUrl, shortForecast, detailedForecast, instruction, sent, status, messageType, category, severity, certainty, urgency, sender, senderName FROM archive WHERE interval = %d AND generatedTime = (SELECT MAX(generatedTime) FROM archive WHERE interval = %d) ORDER BY number" % (NWS.get_interval(forecast_type), NWS.get_interval(forecast_type))
select = "SELECT dateTime, interval, latitude, longitude, usUnits, generatedTime, number, name, startTime, expirationTime, id, endTime, isDaytime, outTemp, outTempTrend, pop, dewpoint, outHumidity, windSpeed, windSpeed2, windDir, iconUrl, shortForecast, detailedForecast, instruction, sent, status, messageType, category, severity, certainty, urgency, sender, senderName, nwsHeadline FROM archive WHERE interval = %d AND generatedTime = (SELECT MAX(generatedTime) FROM archive WHERE interval = %d) ORDER BY number" % (NWS.get_interval(forecast_type), NWS.get_interval(forecast_type))

for row in conn.execute(select):
record = {}
Expand Down Expand Up @@ -1798,6 +1812,7 @@ def print_sqlite_records(conn, dbfile: str, forecast_type: ForecastType, criteri
record['urgency'] = row[31]
record['sender'] = row[32]
record['senderName'] = row[33]
record['nwsHeadline'] = row[34]
pretty_print_record(record, forecast_type)
print('------------------------')

Expand Down Expand Up @@ -1855,6 +1870,8 @@ def pretty_print_forecast(forecast) -> None:
print('sender : %s' % forecast.sender)
if forecast.senderName is not None:
print('senderName : %s' % forecast.senderName)
if forecast.nwsHeadline is not None:
print('nwsHeadline : %s' % forecast.nwsHeadline)

def pretty_print_record(record, forecast_type) -> None:
if forecast_type == ForecastType.ONE_HOUR or forecast_type == ForecastType.TWELVE_HOUR:
Expand Down Expand Up @@ -1904,6 +1921,8 @@ def pretty_print_record(record, forecast_type) -> None:
print('sender : %s' % record['sender'])
if record['senderName'] is not None:
print('senderName : %s' % record['senderName'])
if record['nwsHeadline'] is not None:
print('nwsHeadline : %s' % record['nwsHeadline'])
else: # ForecastType.ALERTS
print('dateTime : %s' % timestamp_to_string(record['dateTime']))
print('Headline : %s' % record['shortForecast'])
Expand Down Expand Up @@ -1936,5 +1955,7 @@ def pretty_print_record(record, forecast_type) -> None:
print('Sender : %s' % record['sender'])
if record['senderName'] is not None:
print('Sender Name : %s' % record['senderName'])
if record['nwsHeadline'] is not None:
print('NWS Headline : %s' % record['nwsHeadline'])

main()
7 changes: 7 additions & 0 deletions changes.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
nws change history
------------------

4.1 06/27/2024
- Add nwsHeadline to alerts.
******* IMPORTANT, you must delete the existing nws database (nws.sdb) if *******
******* udating versions earlier than 4.1. Delete nws.sdb after installing the *******
******* update and just before restaring weewx. This is necessary because the *******
******* database schema has changed. *******

4.0 06/21/2024
- Add the following fields for one hour forecasts:
pop (probabiliby of percipitation)
Expand Down
2 changes: 1 addition & 1 deletion install.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def loader():
class NWSInstaller(ExtensionInstaller):
def __init__(self):
super(NWSInstaller, self).__init__(
version="4.0",
version="4.1",
name='nws',
description='Fetch NWS Hourly Forecast.',
author="John A Kline",
Expand Down
37 changes: 29 additions & 8 deletions skins/nws/forecast_alerts.inc
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@
#for $alert in $nwsforecast.alerts()
#set $alert_count += 1
#set max_title_line = 80
#set headline_length = len($alert.headline)
## If nwsHeadline is None, use headline
#if $alert.nwsHeadline is not None:
#set headline = $alert.nwsHeadline
#else
#set headline = $alert.headline
#end if
#set headline_length = len($headline)
#if $headline_length > $max_title_line
## Figure out how many lines.
#set line_count = math.ceil($headline_length / float($max_title_line))
#set max_title_line = math.ceil($headline_length / $line_count)
#set words = $alert.headline.split()
#set words = $headline.split()
#set lines = []
#set char_count = 0
#set idx = 0
Expand All @@ -34,23 +40,31 @@
#set lines[$idx] += $word
#set char_count += len($word)
#end for
#set headline = ""
#set formatted_headline = ""
#for line in $lines
#if $headline != ""
#set headline += '<br>'
#if $formatted_headline != ""
#set formatted_headline += '<br>'
#end if
#set headline += $line
#set formatted_headline += $line
#end for
#else
#set headline = $alert.headline
#set formatted_headline = $headline
#end if
<tr style='width:100%;'><td style='text-align:center;font-size:$title_font_size;font-weight:bold;border-bottom:1pt solid LightGray;'>$headline</td></tr>
<tr style='width:100%;'><td style='text-align:center;font-size:$title_font_size;font-weight:bold;border-bottom:1pt solid LightGray;'>$formatted_headline</td></tr>
<tr><td style='text-align:left;'><br>Issued: $alert.effective</td></tr>
<tr><td style='text-align:left;'>Onset: $alert.onset</td></tr>
<tr><td style='text-align:left;'>Ends: $alert.ends</td></tr>
<tr><td style='text-align:left;'>Status: $alert.status</td></tr>
<tr><td style='text-align:left;'>Severity: $alert.severity</td></tr>
<tr><td style='text-align:left;'>Certainty: $alert.certainty</td></tr>
#try
#set $nwsHeadline = $alert.nwsHeadline.replace('\n\n', '<br><br>')
#set $nwsHeadline = $nwsHeadline.replace('\n', ' ')
#except
## Calling replace has failed because of missing NWS Headline
## alert.nwsHeadline is probably None
#set $nwsHeadline = $alert.nwsHeadline
#end try
#try
#set $desc = $alert.description.replace('\n\n', '<br><br>')
#set $desc = $desc.replace('\n', ' ')
Expand All @@ -59,6 +73,12 @@
## alert.description is probably None
#set $desc = $alert.description
#end try
<tr>
<td style='text-align:left;'>
<br>
$nwsHeadline
</td>
</tr>
<tr>
<td style='text-align:left;'>
<br>
Expand All @@ -74,6 +94,7 @@
</td>
</tr>
<tr><td style='text-align:left;'>ID: $alert.id</td></tr>
<tr><td style='text-align:left;'>Headline: $alert.headline</td></tr>
<tr><td style='text-align:left;'>Event: $alert.event</td></tr>
<tr><td style='text-align:left;'>Expires: $alert.expires</td></tr>
<tr><td style='text-align:left;'>Sent: $alert.sent</td></tr>
Expand Down

0 comments on commit 57fae83

Please sign in to comment.