diff --git a/pynws/const.py b/pynws/const.py index affd347..5d86582 100644 --- a/pynws/const.py +++ b/pynws/const.py @@ -59,3 +59,33 @@ "blizzard": "Blizzard", "fog": "Fog/mist", } + +OBS_TEMPERATURE = "temperature" +OBS_BARO_PRESSURE = "barometricPressure" +OBS_SEA_PRESSURE = "seaLevelPressure" +OBS_REL_HUMIDITY = "relativeHumidity" +OBS_WIND_SPEED = "windSpeed" +OBS_WIND_DIRECTION = "windDirection" +OBS_VISIBILITY = "visibility" +OBS_ELEVATION = "elevation" +OBS_DESCRIPTION = "textDescription" +OBS_DEWPOINT = "dewpoint" +OBS_WIND_GUST = "windGust" +OBS_STATION = "station" +OBS_TIMESTAMP = "timestamp" +OBS_ICON = "icon" +OBS_MAX_TEMP_24H = "maxTemperatureLast24Hours" +OBS_MIN_TEMP_24H = "minTemperatureLast24Hours" +OBS_PRECIPITATION_1H = "precipitationLastHour" +OBS_PRECIPITATION_3H = "precipitationLast3Hours" +OBS_PRECIPITATION_6H = "precipitationLast6Hours" +OBS_WIND_CHILL = "windChill" +OBS_HEAT_INDEX = "heatIndex" +OBS_RAW_MESSAGE = "rawMessage" + +OBS_ITEM_VALUE = "value" +OBS_ITEM_UNIT_CODE = "unitCode" + +# derived observations +OBS_ICON_TIME = "iconTime" +OBS_ICON_WEATHER = "iconWeather" diff --git a/pynws/simple_nws.py b/pynws/simple_nws.py index ce63970..d275945 100644 --- a/pynws/simple_nws.py +++ b/pynws/simple_nws.py @@ -4,7 +4,36 @@ from metar import Metar -from .const import ALERT_ID, API_WEATHER_CODE +from .const import ( + ALERT_ID, + API_WEATHER_CODE, + OBS_BARO_PRESSURE, + OBS_DESCRIPTION, + OBS_DEWPOINT, + OBS_ELEVATION, + OBS_HEAT_INDEX, + OBS_ICON, + OBS_ICON_TIME, + OBS_ICON_WEATHER, + OBS_ITEM_UNIT_CODE, + OBS_ITEM_VALUE, + OBS_MAX_TEMP_24H, + OBS_MIN_TEMP_24H, + OBS_PRECIPITATION_1H, + OBS_PRECIPITATION_3H, + OBS_PRECIPITATION_6H, + OBS_RAW_MESSAGE, + OBS_REL_HUMIDITY, + OBS_SEA_PRESSURE, + OBS_STATION, + OBS_TEMPERATURE, + OBS_TIMESTAMP, + OBS_VISIBILITY, + OBS_WIND_CHILL, + OBS_WIND_DIRECTION, + OBS_WIND_GUST, + OBS_WIND_SPEED, +) from .nws import Nws WIND_DIRECTIONS = [ @@ -56,27 +85,27 @@ def m_p_s_to_km_p_hr(m_p_s): WIND = {name: idx * 360 / 16 for idx, name in enumerate(WIND_DIRECTIONS)} OBSERVATIONS = { - "temperature": ["temp", "C", None], - "barometricPressure": None, - "seaLevelPressure": ["press", "HPA", 100], - "relativeHumidity": None, - "windSpeed": ["wind_speed", "MPS", 3.6], - "windDirection": ["wind_dir", None, None], - "visibility": ["vis", "M", None], - "elevation": None, - "textDescription": None, - "dewpoint": None, - "windGust": None, - "station": None, - "timestamp": None, - "icon": None, - "maxTemperatureLast24Hours": None, - "minTemperatureLast24Hours": None, - "precipitationLastHour": None, - "precipitationLast3Hours": None, - "precipitationLast6Hours": None, - "windChill": None, - "heatIndex": None, + OBS_TEMPERATURE: ["temp", "C", None], + OBS_BARO_PRESSURE: None, + OBS_SEA_PRESSURE: ["press", "HPA", 100], + OBS_REL_HUMIDITY: None, + OBS_WIND_SPEED: ["wind_speed", "MPS", 3.6], + OBS_WIND_DIRECTION: ["wind_dir", None, None], + OBS_VISIBILITY: ["vis", "M", None], + OBS_ELEVATION: None, + OBS_DESCRIPTION: None, + OBS_DEWPOINT: None, + OBS_WIND_GUST: None, + OBS_STATION: None, + OBS_TIMESTAMP: None, + OBS_ICON: None, + OBS_MAX_TEMP_24H: None, + OBS_MIN_TEMP_24H: None, + OBS_PRECIPITATION_1H: None, + OBS_PRECIPITATION_3H: None, + OBS_PRECIPITATION_6H: None, + OBS_WIND_CHILL: None, + OBS_HEAT_INDEX: None, } @@ -153,7 +182,7 @@ async def set_station(self, station=None): @staticmethod def extract_metar(obs): """Return parsed metar if available.""" - metar_msg = obs.get("rawMessage") + metar_msg = obs.get(OBS_RAW_MESSAGE) if metar_msg: try: metar_obs = Metar.Metar(metar_msg) @@ -170,7 +199,7 @@ async def update_observation(self, limit=0, start_time=None): return None self._observation = sorted( obs, - key=lambda item: self.extract_observation_value(item, "timestamp"), + key=lambda item: self.extract_observation_value(item, OBS_TIMESTAMP), reverse=True, ) self._metar_obs = [self.extract_metar(iobs) for iobs in self._observation] @@ -250,10 +279,10 @@ def extract_observation_value(observation, value): if obs_value is None: return None if isinstance(observation[value], dict): - obs_sub_value = observation[value].get("value") + obs_sub_value = observation[value].get(OBS_ITEM_VALUE) if obs_sub_value is None: return None - return float(obs_sub_value), observation[value].get("unitCode") + return float(obs_sub_value), observation[value].get(OBS_ITEM_UNIT_CODE) return observation[value] @property @@ -285,13 +314,13 @@ def observation(self): data[obs] = met_prop.value() if met[2] is not None: data[obs] = data[obs] * met[2] - if data.get("icon"): - time, weather = parse_icon(data["icon"]) - data["iconTime"] = time - data["iconWeather"] = convert_weather(weather) + if data.get(OBS_ICON): + time, weather = parse_icon(data[OBS_ICON]) + data[OBS_ICON_TIME] = time + data[OBS_ICON_WEATHER] = convert_weather(weather) else: - data["iconTime"] = None - data["iconWeather"] = None + data[OBS_ICON_TIME] = None + data[OBS_ICON_WEATHER] = None return data @property diff --git a/tests/test_simple_nws.py b/tests/test_simple_nws.py index 9f9d3b6..ac70348 100644 --- a/tests/test_simple_nws.py +++ b/tests/test_simple_nws.py @@ -2,6 +2,19 @@ import pytest from pynws import NwsError, SimpleNWS +from pynws.const import ( + OBS_DEWPOINT, + OBS_REL_HUMIDITY, + OBS_SEA_PRESSURE, + OBS_TEMPERATURE, + OBS_VISIBILITY, + OBS_WIND_DIRECTION, + OBS_WIND_GUST, + OBS_WIND_SPEED, + OBS_ICON_WEATHER, + OBS_ICON_TIME, +) + from tests.helpers import data_return_function, setup_app LATLON = (0, 0) @@ -48,17 +61,17 @@ async def test_nws_observation(aiohttp_client, loop, mock_urls, observation_json await nws.update_observation() observation = nws.observation assert observation - assert observation["temperature"] == 10 - assert observation["dewpoint"] == 10 - assert observation["relativeHumidity"] == 10 - assert observation["windDirection"] == 10 - assert observation["visibility"] == 10000 - assert observation["seaLevelPressure"] == 100000 - assert observation["windSpeed"] == 36 # converted to km_gr - assert observation["iconTime"] == "day" - assert observation["windGust"] == 36 # same - assert observation["iconWeather"][0][0] == "A few clouds" - assert observation["iconWeather"][0][1] is None + assert observation[OBS_TEMPERATURE] == 10 + assert observation[OBS_DEWPOINT] == 10 + assert observation[OBS_REL_HUMIDITY] == 10 + assert observation[OBS_WIND_DIRECTION] == 10 + assert observation[OBS_VISIBILITY] == 10000 + assert observation[OBS_SEA_PRESSURE] == 100000 + assert observation[OBS_WIND_SPEED] == 36 # converted to km_gr + assert observation[OBS_ICON_TIME] == "day" + assert observation[OBS_WIND_GUST] == 36 # same + assert observation[OBS_ICON_WEATHER][0][0] == "A few clouds" + assert observation[OBS_ICON_WEATHER][0][1] is None async def test_nws_observation_units(aiohttp_client, loop, mock_urls): @@ -69,9 +82,9 @@ async def test_nws_observation_units(aiohttp_client, loop, mock_urls): await nws.update_observation() observation = nws.observation assert observation - assert round(observation["temperature"], 1) == -12.2 - assert observation["windSpeed"] == 10 # converted to km_gr - assert observation["windGust"] == 10 + assert round(observation[OBS_TEMPERATURE], 1) == -12.2 + assert observation[OBS_WIND_SPEED] == 10 # converted to km_gr + assert observation[OBS_WIND_GUST] == 10 async def test_nws_observation_metar(aiohttp_client, loop, mock_urls): @@ -82,14 +95,14 @@ async def test_nws_observation_metar(aiohttp_client, loop, mock_urls): await nws.update_observation() observation = nws.observation - assert observation["temperature"] == 25.6 - assert observation["dewpoint"] is None - assert observation["relativeHumidity"] is None - assert observation["windDirection"] == 350.0 - assert observation["visibility"] == 16093.44 - assert round(observation["seaLevelPressure"]) == 101761 - assert round(observation["windSpeed"], 2) == 9.26 - assert observation["windGust"] is None + assert observation[OBS_TEMPERATURE] == 25.6 + assert observation[OBS_DEWPOINT] is None + assert observation[OBS_REL_HUMIDITY] is None + assert observation[OBS_WIND_DIRECTION] == 350.0 + assert observation[OBS_VISIBILITY] == 16093.44 + assert round(observation[OBS_SEA_PRESSURE]) == 101761 + assert round(observation[OBS_WIND_SPEED], 2) == 9.26 + assert observation[OBS_WIND_GUST] is None async def test_nws_observation_metar_noparse(aiohttp_client, loop, mock_urls): @@ -99,7 +112,7 @@ async def test_nws_observation_metar_noparse(aiohttp_client, loop, mock_urls): await nws.set_station(STATION) await nws.update_observation() observation = nws.observation - assert observation["temperature"] is None + assert observation[OBS_TEMPERATURE] is None async def test_nws_observation_empty(aiohttp_client, loop, mock_urls): @@ -110,16 +123,16 @@ async def test_nws_observation_empty(aiohttp_client, loop, mock_urls): await nws.update_observation() observation = nws.observation - assert observation["temperature"] is None - assert observation["dewpoint"] is None - assert observation["relativeHumidity"] is None - assert observation["windDirection"] is None - assert observation["visibility"] is None - assert observation["seaLevelPressure"] is None - assert observation["windSpeed"] is None - assert observation["windGust"] is None - assert observation["iconTime"] is None - assert observation["iconWeather"] is None + assert observation[OBS_TEMPERATURE] is None + assert observation[OBS_DEWPOINT] is None + assert observation[OBS_REL_HUMIDITY] is None + assert observation[OBS_WIND_DIRECTION] is None + assert observation[OBS_VISIBILITY] is None + assert observation[OBS_SEA_PRESSURE] is None + assert observation[OBS_WIND_SPEED] is None + assert observation[OBS_WIND_GUST] is None + assert observation[OBS_ICON_TIME] is None + assert observation[OBS_ICON_WEATHER] is None async def test_nws_observation_noprop(aiohttp_client, loop, mock_urls): @@ -141,16 +154,16 @@ async def test_nws_observation_missing_value(aiohttp_client, loop, mock_urls): await nws.update_observation() observation = nws.observation - assert observation["temperature"] is None - assert observation["dewpoint"] is None - assert observation["relativeHumidity"] is None - assert observation["windDirection"] is None - assert observation["visibility"] is None - assert observation["seaLevelPressure"] is None - assert observation["windSpeed"] is None - assert observation["windGust"] is None - assert observation["iconTime"] is None - assert observation["iconWeather"] is None + assert observation[OBS_TEMPERATURE] is None + assert observation[OBS_DEWPOINT] is None + assert observation[OBS_REL_HUMIDITY] is None + assert observation[OBS_WIND_DIRECTION] is None + assert observation[OBS_VISIBILITY] is None + assert observation[OBS_SEA_PRESSURE] is None + assert observation[OBS_WIND_SPEED] is None + assert observation[OBS_WIND_GUST] is None + assert observation[OBS_ICON_TIME] is None + assert observation[OBS_ICON_WEATHER] is None @freeze_time("2019-10-13T14:30:00-04:00") @@ -161,10 +174,10 @@ async def test_nws_forecast(aiohttp_client, loop, mock_urls): await nws.update_forecast() forecast = nws.forecast - assert forecast[0]["iconWeather"][0][0] == "Thunderstorm (high cloud cover)" - assert forecast[0]["iconWeather"][0][1] == 40 - assert forecast[0]["iconWeather"][1][0] == "Overcast" - assert forecast[0]["iconWeather"][1][1] is None + assert forecast[0][OBS_ICON_WEATHER][0][0] == "Thunderstorm (high cloud cover)" + assert forecast[0][OBS_ICON_WEATHER][0][1] == 40 + assert forecast[0][OBS_ICON_WEATHER][1][0] == "Overcast" + assert forecast[0][OBS_ICON_WEATHER][1][1] is None assert forecast[0]["windSpeedAvg"] == 10 assert forecast[0]["windBearing"] == 180 @@ -176,13 +189,13 @@ async def test_nws_forecast_discard_stale(aiohttp_client, loop, mock_urls): nws = SimpleNWS(*LATLON, USERID, client, filter_forecast=True) await nws.update_forecast_hourly() forecast = nws.forecast_hourly - assert forecast[0]["temperature"] == 77 + assert forecast[0][OBS_TEMPERATURE] == 77 nws = SimpleNWS(*LATLON, USERID, client, filter_forecast=False) await nws.update_forecast_hourly() forecast = nws.forecast_hourly - assert forecast[0]["temperature"] == 78 + assert forecast[0][OBS_TEMPERATURE] == 78 @freeze_time("2019-10-14T20:30:00-04:00") @@ -193,7 +206,7 @@ async def test_nws_forecast_hourly(aiohttp_client, loop, mock_urls): await nws.update_forecast_hourly() forecast = nws.forecast_hourly - assert forecast[0]["temperature"] == 78 + assert forecast[0][OBS_TEMPERATURE] == 78 @freeze_time("2019-10-13T14:30:00-04:00") @@ -204,10 +217,10 @@ async def test_nws_forecast_strings(aiohttp_client, loop, mock_urls): await nws.update_forecast() forecast = nws.forecast - assert forecast[0]["iconWeather"][0][0] == "Thunderstorm (high cloud cover)" - assert forecast[0]["iconWeather"][0][1] == 40 - assert forecast[0]["iconWeather"][1][0] == "Overcast" - assert forecast[0]["iconWeather"][1][1] is None + assert forecast[0][OBS_ICON_WEATHER][0][0] == "Thunderstorm (high cloud cover)" + assert forecast[0][OBS_ICON_WEATHER][0][1] == 40 + assert forecast[0][OBS_ICON_WEATHER][1][0] == "Overcast" + assert forecast[0][OBS_ICON_WEATHER][1][1] is None assert forecast[0]["windSpeedAvg"] == 10 assert forecast[0]["windBearing"] == 180