feat: add French language support and update config example

main
swanie98635 2 months ago
parent 118cf5216c
commit d04f4f8fc6

@ -20,7 +20,15 @@ class AudioHandler:
if os.path.exists(filepath):
os.remove(filepath)
cmd = self.tts_template.format(file=filepath, text=text)
# Resolve language code
lang_cfg = self.config.get('language', 'en')
lang_map = {
'en': 'en-US',
'fr': 'fr-FR'
}
lang_code = lang_map.get(lang_cfg, 'en-US')
cmd = self.tts_template.format(file=filepath, text=text, lang=lang_code)
self.logger.info(f"Generating audio: {cmd}")
try:

@ -37,7 +37,7 @@ def do_full_report(config):
sun_info = astro.get_astro_info(loc_info)
# Narrate
narrator = Narrator()
narrator = Narrator(config)
text = narrator.build_full_report(loc_info, conditions, forecast, alerts, sun_info)
logger.info(f"Report Text: {text}")
@ -54,7 +54,7 @@ def monitor_loop(config):
known_alerts = set()
loc_svc = LocationService(config)
narrator = Narrator()
narrator = Narrator(config)
handler = AudioHandler(config)
nodes = config.get('audio', {}).get('nodes', [])
prov_code = config.get('location', {}).get('provider')

@ -1,79 +1,117 @@
from datetime import datetime
import pytz
from typing import List
from typing import List, Dict
from .models import LocationInfo, CurrentConditions, WeatherForecast, WeatherAlert
class Narrator:
def __init__(self):
pass
STRINGS = {
'en': {
'wind_fmt': ", with winds from the {dir} at {kph} kilometers per hour, or {mph} miles per hour",
'current_intro': "Current conditions for {city}, {region}.",
'temp_fmt': "The temperature is {temp_c} degrees celsius, {temp_f} degrees fahrenheit.",
'conditions_fmt': "Conditions are {desc}{wind}.",
'forecast_intro': "Here is the forecast. ",
'high_fmt': " with a high of {c} celsius, {f} fahrenheit",
'low_fmt': " with a low of {c} celsius, {f} fahrenheit",
'period_fmt': "{period}: {summary}{temp}. ",
'no_alerts': "There are no active weather alerts.",
'alerts_intro': "There are {count} active weather alerts. ",
'alert_item': "A {title} is in effect until {expires}. ",
'greeting': "Good day. The time is {time}.",
'intro_city': "This is the automated weather report for {city}.",
'alert_advise': "Please be advised: ",
'time_fmt': "%I %M %p"
},
'fr': {
'wind_fmt': ", avec des vents du {dir} à {kph} kilomètres à l'heure",
'current_intro': "Conditions actuelles pour {city}, {region}.",
'temp_fmt': "La température est de {temp_c} degrés Celsius, {temp_f} degrés Fahrenheit.",
'conditions_fmt': "Les conditions sont {desc}{wind}.",
'forecast_intro': "Voici les prévisions. ",
'high_fmt': " avec un maximum de {c} Celsius, {f} Fahrenheit",
'low_fmt': " avec un minimum de {c} Celsius, {f} Fahrenheit",
'period_fmt': "{period}: {summary}{temp}. ",
'no_alerts': "Il n'y a aucune alerte météo en vigueur.",
'alerts_intro': "Il y a {count} alertes météo en vigueur. ",
'alert_item': "Un {title} est en vigueur jusqu'à {expires}. ",
'greeting': "Bonjour. Il est {time}.",
'intro_city': "Ceci est le bulletin météo automatisé pour {city}.",
'alert_advise': "Veuillez noter : ",
'time_fmt': "%H heures %M"
}
}
def __init__(self, config: Dict):
self.lang = config.get('language', 'en')
self.s = self.STRINGS.get(self.lang, self.STRINGS['en'])
def _c_to_f(self, temp_c: float) -> int:
return int((temp_c * 9/5) + 32)
def _t(self, key, **kwargs):
tpl = self.s.get(key, "")
return tpl.format(**kwargs)
def announce_conditions(self, loc: LocationInfo, current: CurrentConditions) -> str:
wind = ""
if current.wind_speed and current.wind_speed > 5:
# Describe wind speed in km/h and mph
mph = int(current.wind_speed * 0.621371)
wind = f", with winds from the {current.wind_direction} at {int(current.wind_speed)} kilometers per hour, or {mph} miles per hour"
wind = self._t('wind_fmt', dir=current.wind_direction, kph=int(current.wind_speed), mph=mph)
intro = self._t('current_intro', city=loc.city, region=loc.region)
temp_str = self._t('temp_fmt', temp_c=int(current.temperature), temp_f=self._c_to_f(current.temperature))
cond_str = self._t('conditions_fmt', desc=current.description, wind=wind)
return (
f"Current conditions for {loc.city}, {loc.region}. "
f"The temperature is {int(current.temperature)} degrees celsius, {self._c_to_f(current.temperature)} degrees fahrenheit. "
f"Conditions are {current.description}{wind}."
)
return f"{intro} {temp_str} {cond_str}"
def announce_forecast(self, forecasts: List[WeatherForecast]) -> str:
if not forecasts:
return ""
text = "Here is the forecast. "
for f in forecasts[:3]: # Read first 3 periods
text = self._t('forecast_intro')
for f in forecasts[:3]:
temp = ""
if f.high_temp is not None:
temp = f" with a high of {int(f.high_temp)} celsius, {self._c_to_f(f.high_temp)} fahrenheit"
temp = self._t('high_fmt', c=int(f.high_temp), f=self._c_to_f(f.high_temp))
elif f.low_temp is not None:
temp = f" with a low of {int(f.low_temp)} celsius, {self._c_to_f(f.low_temp)} fahrenheit"
temp = self._t('low_fmt', c=int(f.low_temp), f=self._c_to_f(f.low_temp))
text += f"{f.period_name}: {f.summary}{temp}. "
text += self._t('period_fmt', period=f.period_name, summary=f.summary, temp=temp)
return text
def announce_alerts(self, alerts: List[WeatherAlert]) -> str:
if not alerts:
return "There are no active weather alerts."
return self._t('no_alerts')
text = f"There are {len(alerts)} active weather alerts. "
text = self._t('alerts_intro', count=len(alerts))
for a in alerts:
# "A Severe Thunderstorm Warning is in effect until 5 PM."
expires_str = a.expires.strftime("%I %M %p")
text += f"A {a.title} is in effect until {expires_str}. "
# Need locale specific time format?
# Alerts usually have specific expiration.
expires_str = a.expires.strftime(self.s.get('time_fmt', "%I %M %p"))
text += self._t('alert_item', title=a.title, expires=expires_str)
return text
def build_full_report(self, loc: LocationInfo, current: CurrentConditions, forecast: List[WeatherForecast], alerts: List[WeatherAlert], sun_info: str = "") -> str:
parts = []
# Localize time
try:
tz = pytz.timezone(loc.timezone)
now = datetime.now(tz)
except Exception:
now = datetime.now()
# Time string: "10 30 PM"
now_str = now.strftime("%I %M %p")
# Remove leading zero from hour if desired, but TTS usually handles "09" fine.
# For cleaner TTS:
if now_str.startswith("0"):
time_fmt = self.s.get('time_fmt', "%I %M %p")
now_str = now.strftime(time_fmt)
if self.lang == 'en' and now_str.startswith("0"):
now_str = now_str[1:]
parts.append(f"Good day. The time is {now_str}.")
parts.append(f"This is the automated weather report for {loc.city}.")
parts.append(self._t('greeting', time=now_str))
parts.append(self._t('intro_city', city=loc.city))
if alerts:
parts.append("Please be advised: " + self.announce_alerts(alerts))
parts.append(self._t('alert_advise') + self.announce_alerts(alerts))
parts.append(self.announce_conditions(loc, current))
parts.append(self.announce_forecast(forecast))

@ -9,9 +9,12 @@ class ECProvider(WeatherProvider):
self.points_cache = {}
self.extra_zones = kwargs.get('alerts', {}).get('extra_zones', [])
# Map config language code (fr/en) to ECWeather expected parameter (french/english)
self.language = 'french' if kwargs.get('language') == 'fr' else 'english'
def _get_ec_data(self, lat, lon):
# ECWeather auto-selects station based on lat/lon
ec = ECWeather(coordinates=(lat, lon))
ec = ECWeather(coordinates=(lat, lon), language=self.language)
ec.update()
return ec
@ -62,7 +65,7 @@ class ECProvider(WeatherProvider):
for zone_id in self.extra_zones:
if "/" in zone_id: # Basic check if it looks like EC station ID
try:
ec_objects.append(ECWeather(station_id=zone_id))
ec_objects.append(ECWeather(station_id=zone_id, language=self.language))
except Exception:
pass

@ -4,10 +4,14 @@ location:
# longitude: -75.6972
# provider: CA # Optional: force US or CA
language: en # Options: en, fr
voice:
# Example utilizing pico2wave (sudo apt install libttspico-utils)
tts_command: 'pico2wave -w /tmp/temp.wav "{text}" && sox /tmp/temp.wav -r 8000 -c 1 {file}'
# {lang} is replaced by 'en-US' or 'fr-FR'
tts_command: 'pico2wave -l {lang} -w /tmp/temp.wav "{text}" && sox /tmp/temp.wav -r 8000 -c 1 {file}'
audio:
nodes:
- "YOUR_NODE_NUMBER_HERE"

Loading…
Cancel
Save

Powered by TurnKey Linux.