Add humidity to NWS forecast (#95575)
* Add humidity to NWS forecast to address https://github.com/home-assistant/core/issues/95572 * Use pynws 1.5.0 enhancements for probabilityOfPrecipitation, dewpoint, and relativeHumidity. * Update requirements to match pynws version * test for clear night * update docstring --------- Co-authored-by: MatthewFlamm <39341281+MatthewFlamm@users.noreply.github.com>
This commit is contained in:
parent
e94726ec84
commit
6c4b5291e1
6 changed files with 65 additions and 20 deletions
|
@ -7,5 +7,5 @@
|
|||
"iot_class": "cloud_polling",
|
||||
"loggers": ["metar", "pynws"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pynws==1.4.1"]
|
||||
"requirements": ["pynws==1.5.0"]
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ from homeassistant.components.weather import (
|
|||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_SUNNY,
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_HUMIDITY,
|
||||
ATTR_FORECAST_NATIVE_DEW_POINT,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
|
@ -52,16 +54,13 @@ from .const import (
|
|||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
def convert_condition(
|
||||
time: str, weather: tuple[tuple[str, int | None], ...]
|
||||
) -> tuple[str, int | None]:
|
||||
def convert_condition(time: str, weather: tuple[tuple[str, int | None], ...]) -> str:
|
||||
"""Convert NWS codes to HA condition.
|
||||
|
||||
Choose first condition in CONDITION_CLASSES that exists in weather code.
|
||||
If no match is found, return first condition from NWS
|
||||
"""
|
||||
conditions: list[str] = [w[0] for w in weather]
|
||||
prec_probs = [w[1] or 0 for w in weather]
|
||||
|
||||
# Choose condition with highest priority.
|
||||
cond = next(
|
||||
|
@ -75,10 +74,10 @@ def convert_condition(
|
|||
|
||||
if cond == "clear":
|
||||
if time == "day":
|
||||
return ATTR_CONDITION_SUNNY, max(prec_probs)
|
||||
return ATTR_CONDITION_SUNNY
|
||||
if time == "night":
|
||||
return ATTR_CONDITION_CLEAR_NIGHT, max(prec_probs)
|
||||
return cond, max(prec_probs)
|
||||
return ATTR_CONDITION_CLEAR_NIGHT
|
||||
return cond
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -219,8 +218,7 @@ class NWSWeather(WeatherEntity):
|
|||
time = self.observation.get("iconTime")
|
||||
|
||||
if weather:
|
||||
cond, _ = convert_condition(time, weather)
|
||||
return cond
|
||||
return convert_condition(time, weather)
|
||||
return None
|
||||
|
||||
@property
|
||||
|
@ -256,16 +254,27 @@ class NWSWeather(WeatherEntity):
|
|||
else:
|
||||
data[ATTR_FORECAST_NATIVE_TEMP] = None
|
||||
|
||||
data[ATTR_FORECAST_PRECIPITATION_PROBABILITY] = forecast_entry.get(
|
||||
"probabilityOfPrecipitation"
|
||||
)
|
||||
|
||||
if (dewp := forecast_entry.get("dewpoint")) is not None:
|
||||
data[ATTR_FORECAST_NATIVE_DEW_POINT] = TemperatureConverter.convert(
|
||||
dewp, UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS
|
||||
)
|
||||
else:
|
||||
data[ATTR_FORECAST_NATIVE_DEW_POINT] = None
|
||||
|
||||
data[ATTR_FORECAST_HUMIDITY] = forecast_entry.get("relativeHumidity")
|
||||
|
||||
if self.mode == DAYNIGHT:
|
||||
data[ATTR_FORECAST_DAYTIME] = forecast_entry.get("isDaytime")
|
||||
|
||||
time = forecast_entry.get("iconTime")
|
||||
weather = forecast_entry.get("iconWeather")
|
||||
if time and weather:
|
||||
cond, precip = convert_condition(time, weather)
|
||||
else:
|
||||
cond, precip = None, None
|
||||
data[ATTR_FORECAST_CONDITION] = cond
|
||||
data[ATTR_FORECAST_PRECIPITATION_PROBABILITY] = precip
|
||||
data[ATTR_FORECAST_CONDITION] = (
|
||||
convert_condition(time, weather) if time and weather else None
|
||||
)
|
||||
|
||||
data[ATTR_FORECAST_WIND_BEARING] = forecast_entry.get("windBearing")
|
||||
wind_speed = forecast_entry.get("windSpeedAvg")
|
||||
|
|
|
@ -1861,7 +1861,7 @@ pynuki==1.6.2
|
|||
pynut2==2.1.2
|
||||
|
||||
# homeassistant.components.nws
|
||||
pynws==1.4.1
|
||||
pynws==1.5.0
|
||||
|
||||
# homeassistant.components.nx584
|
||||
pynx584==0.5
|
||||
|
|
|
@ -1377,7 +1377,7 @@ pynuki==1.6.2
|
|||
pynut2==2.1.2
|
||||
|
||||
# homeassistant.components.nws
|
||||
pynws==1.4.1
|
||||
pynws==1.5.0
|
||||
|
||||
# homeassistant.components.nx584
|
||||
pynx584==0.5
|
||||
|
|
|
@ -3,6 +3,8 @@ from homeassistant.components.nws.const import CONF_STATION
|
|||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_LIGHTNING_RAINY,
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_DEW_POINT,
|
||||
ATTR_FORECAST_HUMIDITY,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_TEMP,
|
||||
ATTR_FORECAST_TIME,
|
||||
|
@ -59,6 +61,9 @@ DEFAULT_OBSERVATION = {
|
|||
"windGust": 20,
|
||||
}
|
||||
|
||||
CLEAR_NIGHT_OBSERVATION = DEFAULT_OBSERVATION.copy()
|
||||
CLEAR_NIGHT_OBSERVATION["iconTime"] = "night"
|
||||
|
||||
SENSOR_EXPECTED_OBSERVATION_METRIC = {
|
||||
"dewpoint": "5",
|
||||
"temperature": "10",
|
||||
|
@ -183,6 +188,9 @@ DEFAULT_FORECAST = [
|
|||
"timestamp": "2019-08-12T23:53:00+00:00",
|
||||
"iconTime": "night",
|
||||
"iconWeather": (("lightning-rainy", 40), ("lightning-rainy", 90)),
|
||||
"probabilityOfPrecipitation": 89,
|
||||
"dewpoint": 4,
|
||||
"relativeHumidity": 75,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -192,7 +200,9 @@ EXPECTED_FORECAST_IMPERIAL = {
|
|||
ATTR_FORECAST_TEMP: 10,
|
||||
ATTR_FORECAST_WIND_SPEED: 10,
|
||||
ATTR_FORECAST_WIND_BEARING: 180,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: 90,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: 89,
|
||||
ATTR_FORECAST_DEW_POINT: 4,
|
||||
ATTR_FORECAST_HUMIDITY: 75,
|
||||
}
|
||||
|
||||
EXPECTED_FORECAST_METRIC = {
|
||||
|
@ -211,7 +221,14 @@ EXPECTED_FORECAST_METRIC = {
|
|||
2,
|
||||
),
|
||||
ATTR_FORECAST_WIND_BEARING: 180,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: 90,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: 89,
|
||||
ATTR_FORECAST_DEW_POINT: round(
|
||||
TemperatureConverter.convert(
|
||||
4, UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS
|
||||
),
|
||||
1,
|
||||
),
|
||||
ATTR_FORECAST_HUMIDITY: 75,
|
||||
}
|
||||
|
||||
NONE_FORECAST = [{key: None for key in DEFAULT_FORECAST[0]}]
|
||||
|
|
|
@ -7,6 +7,7 @@ import pytest
|
|||
|
||||
from homeassistant.components import nws
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_SUNNY,
|
||||
ATTR_FORECAST,
|
||||
DOMAIN as WEATHER_DOMAIN,
|
||||
|
@ -19,6 +20,7 @@ import homeassistant.util.dt as dt_util
|
|||
from homeassistant.util.unit_system import METRIC_SYSTEM, US_CUSTOMARY_SYSTEM
|
||||
|
||||
from .const import (
|
||||
CLEAR_NIGHT_OBSERVATION,
|
||||
EXPECTED_FORECAST_IMPERIAL,
|
||||
EXPECTED_FORECAST_METRIC,
|
||||
NONE_FORECAST,
|
||||
|
@ -97,6 +99,23 @@ async def test_imperial_metric(
|
|||
assert forecast[0].get(key) == value
|
||||
|
||||
|
||||
async def test_night_clear(hass: HomeAssistant, mock_simple_nws, no_sensor) -> None:
|
||||
"""Test with clear-night in observation."""
|
||||
instance = mock_simple_nws.return_value
|
||||
instance.observation = CLEAR_NIGHT_OBSERVATION
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=nws.DOMAIN,
|
||||
data=NWS_CONFIG,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("weather.abc_daynight")
|
||||
assert state.state == ATTR_CONDITION_CLEAR_NIGHT
|
||||
|
||||
|
||||
async def test_none_values(hass: HomeAssistant, mock_simple_nws, no_sensor) -> None:
|
||||
"""Test with none values in observation and forecast dicts."""
|
||||
instance = mock_simple_nws.return_value
|
||||
|
|
Loading…
Add table
Reference in a new issue