You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SkywarnPlus/SkyDescribe.py

207 lines
5.6 KiB

import os
import sys
import requests
import json
from ruamel.yaml import YAML
import urllib.parse
import subprocess
import wave
import contextlib
import re
# Use ruamel.yaml instead of PyYAML
yaml = YAML()
# Directories and Paths
baseDir = os.path.dirname(os.path.realpath(__file__))
configPath = os.path.join(baseDir, "config.yaml")
# Open and read configuration file
with open(configPath, "r") as config_file:
config = yaml.load(config_file)
# Define tmp_dir
tmp_dir = config.get("DEV", {}).get("TmpDir", "/tmp/SkywarnPlus")
# Path to the data file
data_file = os.path.join(tmp_dir, "data.json")
# Enable debugging
debug = True
def debug_print(*args, **kwargs):
"""
Print debug information if debugging is enabled.
"""
if debug:
print(*args, **kwargs)
def load_state():
"""
Load the state from the state file if it exists, else return an initial state.
Returns:
dict: A dictionary containing data.
"""
if os.path.exists(data_file):
with open(data_file, "r") as file:
state = json.load(file)
return state
else:
return {
"ct": None,
"id": None,
"alertscript_alerts": [],
"last_alerts": [],
"last_sayalert": [],
"last_descriptions": {},
}
import re
def modify_description(description):
"""
Modify the description to make it more suitable for conversion to audio.
Args:
description (str): The description text.
Returns:
str: The modified description text.
"""
# Remove newline characters and replace multiple spaces with a single space
description = description.replace('\n', ' ')
description = re.sub(r'\s+', ' ', description)
# Replace some common weather abbreviations and symbols
abbreviations = {
"mph": "miles per hour",
"knots": "nautical miles per hour",
"Nm": "nautical miles",
"nm": "nautical miles",
"PM": "P.M.",
"AM": "A.M.",
"ft.": "feet",
"in.": "inches",
"m": "meter",
"km": "kilometer",
"mi": "mile",
"%": "percent",
"N": "north",
"S": "south",
"E": "east",
"W": "west",
"NE": "northeast",
"NW": "northwest",
"SE": "southeast",
"SW": "southwest",
"F": "Fahrenheit",
"C": "Celsius",
"UV": "ultraviolet",
"gusts up to": "gusts of up to",
"hrs": "hours",
"hr": "hour",
"min": "minute",
"sec": "second",
"sq": "square",
"w/": "with",
"c/o": "care of",
"blw": "below",
"abv": "above",
"avg": "average",
"fr": "from",
"to": "to",
"till": "until",
"b/w": "between",
"btwn": "between",
"N/A": "not available",
"&": "and",
"+": "plus",
"e.g.": "for example",
"i.e.": "that is",
"est.": "estimated",
"...": " dot dot dot ", # or replace with " pause "
"\n\n": " pause ", # or replace with a silence duration
}
for abbr, full in abbreviations.items():
description = description.replace(abbr, full)
# Space out numerical sequences for better pronunciation
description = re.sub(r"(\d)", r"\1 ", description)
# Reform time mentions to a standard format
description = re.sub(r"(\d{1,2})(\d{2}) (A\.M\.|P\.M\.)", r"\1:\2 \3", description)
return description.strip()
def convert_to_audio(api_key, text):
"""
Convert the given text to audio using the Voice RSS Text-to-Speech API.
Args:
api_key (str): The API key.
text (str): The text to convert.
Returns:
str: The path to the audio file.
"""
base_url = 'http://api.voicerss.org/'
params = {
'key': api_key,
'hl': 'en-us',
'src': urllib.parse.quote(text),
'c': 'WAV',
'f': '8khz_8bit_mono'
}
response = requests.get(base_url, params=params)
response.raise_for_status()
audio_file_path = os.path.join(tmp_dir, "description.wav")
with open(audio_file_path, 'wb') as file:
file.write(response.content)
return audio_file_path
def main(index):
state = load_state()
alerts = state["last_alerts"]
descriptions = state["last_descriptions"]
api_key = config["SkyDescribe"]["APIKey"]
try:
alert = alerts[index][0]
description = descriptions[alert]
except IndexError:
print("No alert at index {}".format(index))
description = "No alert description found at index {}".format(index)
# Modify the description
debug_print("Original description:", description)
description = modify_description(description)
debug_print("Modified description:", description)
# Convert description to audio
audio_file = convert_to_audio(api_key, description)
# Check the length of audio file
with contextlib.closing(wave.open(audio_file,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = frames / float(rate)
debug_print("Length of the audio file in seconds: ", duration)
# Play the corresponding audio message on all nodes
nodes = config["Asterisk"]["Nodes"]
for node in nodes:
command = "/usr/sbin/asterisk -rx 'rpt localplay {} {}'".format(
node, audio_file.rsplit('.', 1)[0]
)
debug_print("Running command:", command)
subprocess.run(command, shell=True)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: SkyDescribe.py <alert index>")
sys.exit(1)
main(int(sys.argv[1]))

Powered by TurnKey Linux.