From 2a506bc327cd6a3b3676628afad67ef6115fc439 Mon Sep 17 00:00:00 2001 From: agessaman Date: Sat, 3 Oct 2026 22:50:52 -0700 Subject: [PATCH] refactor(weather): one reader for the Open-Meteo unit settings S7's units item. GlobalWxCommand and WeatherService each read [Weather] temperature_unit, wind_speed_unit and precipitation_unit, lowercased them and fell back to fahrenheit/mph/inch with the same three warnings; weather_common.load_open_meteo_units does that once, next to load_open_meteo_model. Both still accept kn. wx keeps its own NOAA unit reading (_unit_setting), which has different fallbacks and sources. Old-vs-new over 5,000 random configs matched on values and warnings. --- .../commands/alternatives/wx_international.py | 27 ++++++++----------- modules/service_plugins/weather_service.py | 17 +++--------- modules/weather_common.py | 22 +++++++++++++++ 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/modules/commands/alternatives/wx_international.py b/modules/commands/alternatives/wx_international.py index 27c945d..875068a 100644 --- a/modules/commands/alternatives/wx_international.py +++ b/modules/commands/alternatives/wx_international.py @@ -28,7 +28,13 @@ from ...utils import ( # noqa: F401 format_temperature_high_low and get_nomina normalize_us_state, rate_limited_nominatim_reverse_sync, ) -from ...weather_common import _ARROWS_8, _COMPASS_16, WeatherCommandMixin, load_open_meteo_model +from ...weather_common import ( + _ARROWS_8, + _COMPASS_16, + WeatherCommandMixin, + load_open_meteo_model, + load_open_meteo_units, +) from ..base_command import BaseCommand # Kept for code that checked them; these imports used to be optional. @@ -100,21 +106,10 @@ class GlobalWxCommand(WeatherCommandMixin, BaseCommand): self.default_country = self.bot.config.get('Weather', 'default_country', fallback='US') self.always_show_location = self.bot.config.getboolean('Weather', 'always_show_location', fallback=False) - # Get unit preferences from config - self.temperature_unit = self.bot.config.get('Weather', 'temperature_unit', fallback='fahrenheit').lower() - self.wind_speed_unit = self.bot.config.get('Weather', 'wind_speed_unit', fallback='mph').lower() - self.precipitation_unit = self.bot.config.get('Weather', 'precipitation_unit', fallback='inch').lower() - - # Validate units - if self.temperature_unit not in ['fahrenheit', 'celsius']: - self.logger.warning(f"Invalid temperature_unit '{self.temperature_unit}', using 'fahrenheit'") - self.temperature_unit = 'fahrenheit' - if self.wind_speed_unit not in ['mph', 'kmh', 'ms', 'kn']: - self.logger.warning(f"Invalid wind_speed_unit '{self.wind_speed_unit}', using 'mph'") - self.wind_speed_unit = 'mph' - if self.precipitation_unit not in ['inch', 'mm']: - self.logger.warning(f"Invalid precipitation_unit '{self.precipitation_unit}', using 'inch'") - self.precipitation_unit = 'inch' + # Get unit preferences from config (validated) + self.temperature_unit, self.wind_speed_unit, self.precipitation_unit = load_open_meteo_units( + self.bot.config, self.logger + ) # Initialize geocoder (will use rate-limited helpers for actual calls) self.geolocator = get_nominatim_geocoder() diff --git a/modules/service_plugins/weather_service.py b/modules/service_plugins/weather_service.py index 42677c8..722ca0f 100644 --- a/modules/service_plugins/weather_service.py +++ b/modules/service_plugins/weather_service.py @@ -41,7 +41,7 @@ from ..nws_alerts import SERVICE_SPECIAL_RULES, entry_nws_headline, entry_summar from ..nws_coverage import NWSNoCoverageCache from ..url_shortener import shorten_url_sync from ..utils import format_temperature_high_low, get_config_timezone -from ..weather_common import load_open_meteo_model +from ..weather_common import load_open_meteo_model, load_open_meteo_units from .base_service import BaseServicePlugin # Try to import MQTT client (use paho-mqtt like packet capture service) @@ -162,18 +162,9 @@ class WeatherService(BaseServicePlugin): # Get temperature/wind units from config (for Open-Meteo). Normalized and # validated the same way GlobalWxCommand does, so the unit also works as # a translation key for its display label. - self.temperature_unit = self.bot.config.get('Weather', 'temperature_unit', fallback='fahrenheit').lower() - self.wind_speed_unit = self.bot.config.get('Weather', 'wind_speed_unit', fallback='mph').lower() - self.precipitation_unit = self.bot.config.get('Weather', 'precipitation_unit', fallback='inch').lower() - if self.temperature_unit not in ('fahrenheit', 'celsius'): - self.logger.warning(f"Invalid temperature_unit '{self.temperature_unit}', using 'fahrenheit'") - self.temperature_unit = 'fahrenheit' - if self.wind_speed_unit not in ('mph', 'kmh', 'ms', 'kn'): - self.logger.warning(f"Invalid wind_speed_unit '{self.wind_speed_unit}', using 'mph'") - self.wind_speed_unit = 'mph' - if self.precipitation_unit not in ('inch', 'mm'): - self.logger.warning(f"Invalid precipitation_unit '{self.precipitation_unit}', using 'inch'") - self.precipitation_unit = 'inch' + self.temperature_unit, self.wind_speed_unit, self.precipitation_unit = load_open_meteo_units( + self.bot.config, self.logger + ) # Proactive rain nowcast ("rain incoming" push). Reuses the rain command's # Open-Meteo 15-minutely logic for the bot's own position. diff --git a/modules/weather_common.py b/modules/weather_common.py index 0c8c595..8617aff 100644 --- a/modules/weather_common.py +++ b/modules/weather_common.py @@ -43,6 +43,28 @@ def load_open_meteo_model(config: Any, logger: Any) -> Optional[str]: return model +def load_open_meteo_units(config: Any, logger: Any) -> tuple[str, str, str]: + """[Weather] temperature_unit, wind_speed_unit and precipitation_unit for Open-Meteo. + + Lowercased; a value Open-Meteo does not accept falls back to fahrenheit, + mph or inch with a warning. The result doubles as a translation key for the + unit's display label. + """ + temperature_unit = config.get('Weather', 'temperature_unit', fallback='fahrenheit').lower() + wind_speed_unit = config.get('Weather', 'wind_speed_unit', fallback='mph').lower() + precipitation_unit = config.get('Weather', 'precipitation_unit', fallback='inch').lower() + if temperature_unit not in ('fahrenheit', 'celsius'): + logger.warning(f"Invalid temperature_unit '{temperature_unit}', using 'fahrenheit'") + temperature_unit = 'fahrenheit' + if wind_speed_unit not in ('mph', 'kmh', 'ms', 'kn'): + logger.warning(f"Invalid wind_speed_unit '{wind_speed_unit}', using 'mph'") + wind_speed_unit = 'mph' + if precipitation_unit not in ('inch', 'mm'): + logger.warning(f"Invalid precipitation_unit '{precipitation_unit}', using 'inch'") + precipitation_unit = 'inch' + return temperature_unit, wind_speed_unit, precipitation_unit + + # 16-point compass labels, clockwise from north, and the arrow for each 45° sector. _COMPASS_16 = ( "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",