v0.4.2 update

pull/140/head
Mason10198 2 years ago
parent 4b8884ea04
commit 92d956e3d8

@ -1,7 +1,7 @@
#!/usr/bin/python3 #!/usr/bin/python3
""" """
SkyControl v0.4.0 by Mason Nelson SkyControl v0.4.2 by Mason Nelson
================================== ==================================
A Control Script for SkywarnPlus A Control Script for SkywarnPlus

@ -1,7 +1,7 @@
#!/usr/bin/python3 #!/usr/bin/python3
""" """
SkyDescribe v0.4.0 by Mason Nelson SkyDescribe v0.4.2 by Mason Nelson
================================================== ==================================================
Text to Speech conversion for Weather Descriptions Text to Speech conversion for Weather Descriptions
@ -45,25 +45,25 @@ CONFIG_PATH = os.path.join(BASE_DIR, "config.yaml")
# Open and read configuration file # Open and read configuration file
with open(CONFIG_PATH, "r") as config_file: with open(CONFIG_PATH, "r") as config_file:
config = YAML.load(config_file) CONFIG = YAML.load(config_file)
# Define tmp_dir # Define tmp_dir
TMP_DIR = config.get("DEV", []).get("TmpDir", "/tmp/SkywarnPlus") TMP_DIR = CONFIG.get("DEV", []).get("TmpDir", "/tmp/SkywarnPlus")
# Define VoiceRSS settings # Define VoiceRSS settings
# get api key, fellback 150 # get api key, fellback 150
API_KEY = config.get("SkyDescribe", []).get("APIKey", "") API_KEY = CONFIG.get("SkyDescribe", []).get("APIKey", "")
LANGUAGE = config.get("SkyDescribe", []).get("Language", "en-us") LANGUAGE = CONFIG.get("SkyDescribe", []).get("Language", "en-us")
SPEED = config.get("SkyDescribe", []).get("Speed", 0) SPEED = CONFIG.get("SkyDescribe", []).get("Speed", 0)
VOICE = config.get("SkyDescribe", []).get("Voice", "John") VOICE = CONFIG.get("SkyDescribe", []).get("Voice", "John")
MAX_WORDS = config.get("SkyDescribe", []).get("MaxWords", 150) MAX_WORDS = CONFIG.get("SkyDescribe", []).get("MaxWords", 150)
# Path to the data file # Path to the data file
DATA_FILE = os.path.join(TMP_DIR, "data.json") DATA_FILE = os.path.join(TMP_DIR, "data.json")
# Define logger # Define logger
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
if config.get("Logging", []).get("Debug", False): if CONFIG.get("Logging", []).get("Debug", False):
LOGGER.setLevel(logging.DEBUG) LOGGER.setLevel(logging.DEBUG)
else: else:
LOGGER.setLevel(logging.INFO) LOGGER.setLevel(logging.INFO)
@ -286,13 +286,42 @@ def main(index_or_title):
) )
else: else:
alert, alert_data = alerts[index] alert, alert_data = alerts[index]
description = alert_data[0]["description"]
# Count the unique instances of the alert
unique_instances = len(
set((data["description"], data["end_time_utc"]) for data in alert_data)
)
# Modify the description
if unique_instances == 1:
description = alert_data[0]["description"]
else:
description = "{} unique instances of this alert exist. Describing the first instance. {}".format(
unique_instances, alert_data[0]["description"]
)
else: else:
# Argument is not an index, assume it's a title # Argument is not an index, assume it's a title
title = index_or_title title = index_or_title
for alert, alert_data in alerts: for alert, alert_data in alerts:
if alert == title: # Assuming alert is a title if alert == title: # Assuming alert is a title
description = alert_data[0]["description"] # Count the unique instances of the alert
unique_instances = len(
set(
(data["description"], data["end_time_utc"])
for data in alert_data
)
)
# Modify the description
if unique_instances == 1:
description = alert_data[0]["description"]
else:
description = "There are {} unique instances of {}. Describing the first instance. {}".format(
unique_instances,
alert,
alert_data[0]["description"]
)
break break
else: else:
LOGGER.error("SkyDescribe: No alert with title %s found.", title) LOGGER.error("SkyDescribe: No alert with title %s found.", title)
@ -322,7 +351,7 @@ def main(index_or_title):
duration = frames / float(rate) duration = frames / float(rate)
LOGGER.debug("SkyDescribe: Length of the audio file in seconds: %s", duration) LOGGER.debug("SkyDescribe: Length of the audio file in seconds: %s", duration)
nodes = config["Asterisk"]["Nodes"] nodes = CONFIG["Asterisk"]["Nodes"]
for node in nodes: for node in nodes:
LOGGER.info("SkyDescribe: Broadcasting description on node %s.", node) LOGGER.info("SkyDescribe: Broadcasting description on node %s.", node)
command = "/usr/sbin/asterisk -rx 'rpt localplay {} {}'".format( command = "/usr/sbin/asterisk -rx 'rpt localplay {} {}'".format(

@ -1,7 +1,7 @@
#!/usr/bin/python3 #!/usr/bin/python3
""" """
SkywarnPlus v0.4.0 by Mason Nelson SkywarnPlus v0.4.2 by Mason Nelson
=============================================================================== ===============================================================================
SkywarnPlus is a utility that retrieves severe weather alerts from the National SkywarnPlus is a utility that retrieves severe weather alerts from the National
Weather Service and integrates these alerts with an Asterisk/app_rpt based Weather Service and integrates these alerts with an Asterisk/app_rpt based
@ -386,35 +386,50 @@ def get_alerts(countyCodes):
LOGGER.debug("getAlerts: DEV Alert Injection Enabled") LOGGER.debug("getAlerts: DEV Alert Injection Enabled")
injected_alerts = config["DEV"].get("INJECTALERTS", []) injected_alerts = config["DEV"].get("INJECTALERTS", [])
LOGGER.debug("getAlerts: Injecting alerts: %s", injected_alerts) LOGGER.debug("getAlerts: Injecting alerts: %s", injected_alerts)
if injected_alerts is None:
injected_alerts = []
county_codes_cycle = itertools.cycle( max_counties = len(countyCodes) # Assuming countyCodes is a list of counties
countyCodes county_codes_cycle = itertools.cycle(countyCodes)
) # Create an iterator that returns elements from the iterable in a cyclic manner
counter = 0 for alert_info in injected_alerts:
for i, event in enumerate(injected_alerts): if isinstance(alert_info, dict):
last_word = event.split()[-1] alert_title = alert_info.get("Title", "")
specified_counties = alert_info.get("CountyCodes", [])
else:
continue # Ignore if not a dictionary
last_word = alert_title.split()[-1]
severity = severity_mapping_words.get(last_word, 0) severity = severity_mapping_words.get(last_word, 0)
description = "This alert was manually injected as a test." description = "This alert was manually injected as a test."
end_time_str = alert_info.get("EndTime")
county_data = [] county_data = []
for j in range( for county in specified_counties:
i + 1 if county not in countyCodes:
): # Here we increase the number of county codes for each alert LOGGER.error(
end_time_utc = current_time + timedelta(hours=counter + 1) "Specified county code '%s' is not defined in the config. Using next available county code from the config.",
county,
)
county = next(county_codes_cycle)
end_time = (
datetime.strptime(end_time_str, "%Y-%m-%dT%H:%M:%SZ")
if end_time_str
else current_time + timedelta(hours=1)
)
county_data.append( county_data.append(
{ {
"county_code": next(county_codes_cycle), "county_code": county,
"severity": severity, "severity": severity,
"description": description, "description": description,
"end_time_utc": end_time_utc.strftime("%Y-%m-%dT%H:%M:%S.%fZ"), "end_time_utc": end_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
} }
) )
counter += 1 # Increase counter here
alerts[event] = county_data # Add the list of dictionaries to the alert alerts[
alert_title
] = county_data # Add the list of dictionaries to the alert
# We limit the number of alerts to the maximum defined constant. # We limit the number of alerts to the maximum defined constant.
alerts = OrderedDict(list(alerts.items())[:MAX_ALERTS]) alerts = OrderedDict(list(alerts.items())[:MAX_ALERTS])
@ -689,10 +704,7 @@ def say_alerts(alerts):
) )
alert_count = 0 alert_count = 0
for ( for alert, counties in alerts.items():
alert,
counties,
) in alerts.items(): # Now we loop over both alert name and its associated counties
if alert in filtered_alerts: if alert in filtered_alerts:
try: try:
descriptions = [county["description"] for county in counties] descriptions = [county["description"] for county in counties]
@ -722,15 +734,18 @@ def say_alerts(alerts):
) )
alert_count += 1 alert_count += 1
# Add county names if they exist added_county_codes = set()
for county in counties: for county in counties:
# if its the first county, word_space is 600ms of silence. else it is 400ms
if counties.index(county) == 0: if counties.index(county) == 0:
word_space = AudioSegment.silent(duration=600) word_space = AudioSegment.silent(duration=600)
else: else:
word_space = AudioSegment.silent(duration=400) word_space = AudioSegment.silent(duration=400)
county_code = county["county_code"] county_code = county["county_code"]
if COUNTY_WAVS and county_code in COUNTY_CODES: if (
COUNTY_WAVS
and county_code in COUNTY_CODES
and county_code not in added_county_codes
):
index = COUNTY_CODES.index(county_code) index = COUNTY_CODES.index(county_code)
county_name_file = COUNTY_WAVS[index] county_name_file = COUNTY_WAVS[index]
LOGGER.debug( LOGGER.debug(
@ -739,10 +754,17 @@ def say_alerts(alerts):
county_name_file, county_name_file,
alert, alert,
) )
combined_sound += word_space + AudioSegment.from_wav( try:
os.path.join(SOUNDS_PATH, county_name_file) combined_sound += word_space + AudioSegment.from_wav(
) os.path.join(SOUNDS_PATH, county_name_file)
# if this is the last county name, add 600ms of silence after the county name )
except FileNotFoundError:
LOGGER.error(
"sayAlert: County audio file not found: %s",
os.path.join(SOUNDS_PATH, county_name_file),
)
added_county_codes.add(county_code)
if counties.index(county) == len(counties) - 1: if counties.index(county) == len(counties) - 1:
combined_sound += AudioSegment.silent(duration=600) combined_sound += AudioSegment.silent(duration=600)
@ -750,7 +772,7 @@ def say_alerts(alerts):
LOGGER.error("sayAlert: Alert not found: %s", alert) LOGGER.error("sayAlert: Alert not found: %s", alert)
except FileNotFoundError: except FileNotFoundError:
LOGGER.error( LOGGER.error(
"sayAlert: Audio file not found: %s/ALERTS/SWP_%s.wav", "sayAlert: Alert audio file not found: %s/ALERTS/SWP_%s.wav",
SOUNDS_PATH, SOUNDS_PATH,
ALERT_INDEXES[index], ALERT_INDEXES[index],
) )
@ -920,15 +942,16 @@ def build_tailmessage(alerts):
descriptions = [county["description"] for county in counties] descriptions = [county["description"] for county in counties]
end_times = [county["end_time_utc"] for county in counties] end_times = [county["end_time_utc"] for county in counties]
if len(set(descriptions)) > 1 or len(set(end_times)) > 1: if config["Alerting"]["WithMultiples"]:
LOGGER.debug( if len(set(descriptions)) > 1 or len(set(end_times)) > 1:
"buildTailMessage: Found multiple unique instances of the alert %s", LOGGER.debug(
alert, "buildTailMessage: Found multiple unique instances of the alert %s",
) alert,
multiples_sound = AudioSegment.from_wav( )
os.path.join(SOUNDS_PATH, "ALERTS", "SWP_149.wav") multiples_sound = AudioSegment.from_wav(
) os.path.join(SOUNDS_PATH, "ALERTS", "SWP_149.wav")
combined_sound += AudioSegment.silent(duration=200) + multiples_sound )
combined_sound += AudioSegment.silent(duration=200) + multiples_sound
# Add county names if they exist # Add county names if they exist
if county_identifiers: if county_identifiers:

@ -1,7 +1,7 @@
#!/usr/bin/python3 #!/usr/bin/python3
""" """
SkywarnPlus Updater v0.4.0 by Mason Nelson SkywarnPlus Updater v0.4.2 by Mason Nelson
=============================================================================== ===============================================================================
Script to update SkywarnPlus to the latest version. This script will download Script to update SkywarnPlus to the latest version. This script will download
the latest version of SkywarnPlus from GitHub, and then merge the existing the latest version of SkywarnPlus from GitHub, and then merge the existing

@ -1,4 +1,4 @@
# SkywarnPlus v0.4.0 Configuration File # SkywarnPlus v0.4.2 Configuration File
# Author: Mason Nelson (N5LSN/WRKF394) # Author: Mason Nelson (N5LSN/WRKF394)
# Please edit this file according to your specific requirements. # Please edit this file according to your specific requirements.
@ -75,6 +75,10 @@ Alerting:
# Specify the WAV file in the SOUNDS/ALERTS directory to use as the alert seperator sound effect # Specify the WAV file in the SOUNDS/ALERTS directory to use as the alert seperator sound effect
AlertSeperator: Woodblock.wav AlertSeperator: Woodblock.wav
# Enable audio tagging an alert as having "multiples" if there is more than one unique instance of that alert type
# If enabled, and there are 2x different Severe Thunderstorm Warnings in your area, the audio will be: "Severe Thunderstorm Warning, with multiples"
WithMultiples: true
# Limit the maximum number of alerts to process in case of multiple alerts. # Limit the maximum number of alerts to process in case of multiple alerts.
# SkywarnPlus fetches all alerts, orders them by severity, and processes only the 'n' most severe alerts, where 'n' is the MaxAlerts value. # SkywarnPlus fetches all alerts, orders them by severity, and processes only the 'n' most severe alerts, where 'n' is the MaxAlerts value.
MaxAlerts: 99 MaxAlerts: 99
@ -409,8 +413,21 @@ DEV:
# Enable test alert injection instead of calling the NWS API by setting 'INJECT' to 'True'. # Enable test alert injection instead of calling the NWS API by setting 'INJECT' to 'True'.
INJECT: false INJECT: false
# List the test alerts to inject. Use a case-sensitive list. One alert per line. # List the test alerts to inject. Alert titles are case sensitive.
# Optionally specify the CountyCodes and/or EndTime for each alert.
# CountyCodes used here must be defined at the top of this configuration file.
# Example:
# INJECTALERTS:
# - Title: "Tornado Warning"
# CountyCodes: ["ARC119", "ARC120"]
# - Title: "Tornado Watch"
# CountyCodes: ["ARC125"]
# EndTime: "2023-08-01T12:00:00Z"
# - Title: "Severe Thunderstorm Warning"
INJECTALERTS: INJECTALERTS:
- Tornado Warning - Title: "Tornado Warning"
- Tornado Watch CountyCodes: ["ARC119", "ARC120"]
- Severe Thunderstorm Warning - Title: "Tornado Watch"
CountyCodes: ["ARC125"]
- Title: "Severe Thunderstorm Warning"
CountyCodes: ["ARC085"]
Loading…
Cancel
Save

Powered by TurnKey Linux.