Add metar fallback.

Use metar code from nws observation if normal api is missing data.
This commit is contained in:
ktdad
2019-05-19 07:41:30 -04:00
parent 1f437a364b
commit 1e8bae264a
4 changed files with 124 additions and 10 deletions

View File

@@ -4,5 +4,8 @@
"documentation": "https://www.home-assistant.io/components/nws",
"dependencies": [],
"codeowners": ["@MatthewFlamm"],
"requirements": ["pynws==0.6"]
"requirements": [
"pynws==0.6",
"metar==1.7.0"
]
}

View File

@@ -22,8 +22,6 @@ from homeassistant.util.distance import convert as convert_distance
from homeassistant.util.pressure import convert as convert_pressure
from homeassistant.util.temperature import convert as convert_temperature
REQUIREMENTS = ['pynws==0.6']
_LOGGER = logging.getLogger(__name__)
ATTRIBUTION = 'Data from National Weather Service/NOAA'
@@ -127,7 +125,7 @@ async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up the NWS weather platform."""
from pynws import Nws
from metar import Metar
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
station = config.get(CONF_STATION)
@@ -155,16 +153,21 @@ async def async_setup_platform(hass, config, async_add_entities,
nws.station = station
_LOGGER.debug("Initialized station %s", station[0])
async_add_entities([NWSWeather(nws, hass.config.units, config)], True)
async_add_entities(
[NWSWeather(nws, Metar.Metar, hass.config.units, config)],
True)
class NWSWeather(WeatherEntity):
"""Representation of a weather condition."""
def __init__(self, nws, units, config):
def __init__(self, nws, metar, units, config):
"""Initialise the platform with a data instance and station name."""
self._nws = nws
self._metar = metar
self._station_name = config.get(CONF_NAME, self._nws.station)
self._metar_obs = None
self._observation = None
self._forecast = None
self._description = None
@@ -178,6 +181,11 @@ class NWSWeather(WeatherEntity):
_LOGGER.debug("Updating station observations %s",
self._nws.station)
self._observation = await self._nws.observations()
self._metar_obs = [
self._metar(obs['rawMessage'])
for obs in self._observation
if 'rawMessage' in obs.keys()
]
_LOGGER.debug("Updating forecast")
if self._mode == 'daynight':
self._forecast = await self._nws.forecast()
@@ -200,6 +208,8 @@ class NWSWeather(WeatherEntity):
def temperature(self):
"""Return the current temperature."""
temp_c = self._observation[0]['temperature']['value']
if temp_c is None and self._metar_obs:
temp_c = self._metar_obs[0].temp.value(units='C')
if temp_c is not None:
return convert_temperature(temp_c, TEMP_CELSIUS, TEMP_FAHRENHEIT)
return None
@@ -208,9 +218,13 @@ class NWSWeather(WeatherEntity):
def pressure(self):
"""Return the current pressure."""
pressure_pa = self._observation[0]['seaLevelPressure']['value']
# convert Pa to in Hg
if pressure_pa is None:
if pressure_pa is None and self._metar_obs:
pressure_hpa = self._metar_obs[0].press.value(units='HPA')
if pressure_hpa is None:
return None
pressure_pa = convert_pressure(pressure_hpa, PRESSURE_HPA,
PRESSURE_PA)
if self._is_metric:
pressure = convert_pressure(pressure_pa, PRESSURE_PA, PRESSURE_HPA)
@@ -230,6 +244,9 @@ class NWSWeather(WeatherEntity):
def wind_speed(self):
"""Return the current windspeed."""
wind_m_s = self._observation[0]['windSpeed']['value']
if wind_m_s is None and self._metar_obs:
wind_m_s = self._metar_obs[0].wind_speed.value(units='MPS')
print(wind_m_s)
if wind_m_s is None:
return None
wind_m_hr = wind_m_s * 3600
@@ -244,7 +261,10 @@ class NWSWeather(WeatherEntity):
@property
def wind_bearing(self):
"""Return the current wind bearing (degrees)."""
return self._observation[0]['windDirection']['value']
wind_bearing = self._observation[0]['windDirection']['value']
if wind_bearing is None and self._metar_obs:
wind_bearing = self._metar_obs[0].wind_dir.value()
return wind_bearing
@property
def temperature_unit(self):
@@ -262,6 +282,8 @@ class NWSWeather(WeatherEntity):
def visibility(self):
"""Return visibility."""
vis_m = self._observation[0]['visibility']['value']
if vis_m is None and self._metar_obs:
vis_m = self._metar_obs[0].vis.value(units='M')
if vis_m is None:
return None

View File

@@ -765,6 +765,9 @@ mbddns==0.1.2
# homeassistant.components.message_bird
messagebird==1.2.0
# homeassistant.components.nws
metar==1.7.0
# homeassistant.components.meteoalarm
meteoalertapi==0.1.5

View File

@@ -40,6 +40,21 @@ OBS = [{
'textDescription': 'Sunny'
}]
METAR_MSG = ("PHNG 182257Z 06012KT 10SM FEW020 SCT026 SCT035 "
"28/22 A3007 RMK AO2 SLP177 T02780217")
OBS_METAR = [{
"rawMessage": METAR_MSG,
"textDescription": "Partly Cloudy",
"icon": "https://api.weather.gov/icons/land/day/sct?size=medium",
"temperature": {"value": None, "qualityControl": "qc:Z"},
"windDirection": {"value": None, "qualityControl": "qc:Z"},
"windSpeed": {"value": None, "qualityControl": "qc:Z"},
"seaLevelPressure": {"value": None, "qualityControl": "qc:Z"},
"visibility": {"value": None, "qualityControl": "qc:Z"},
"relativeHumidity": {"value": None, "qualityControl": "qc:Z"},
}]
FORE = [{
'endTime': '2018-12-21T18:00:00-05:00',
'windSpeed': '8 to 10 mph',
@@ -306,3 +321,74 @@ class TestNwsMetric(unittest.TestCase):
assert forecast[0].get(ATTR_FORECAST_WIND_BEARING) == 180
assert forecast[0].get(ATTR_FORECAST_WIND_SPEED) == round(
convert_distance(9, LENGTH_MILES, LENGTH_KILOMETERS))
class MockNws_Metar():
"""Mock Station from pynws."""
def __init__(self, websession, latlon, userid):
"""Init mock nws."""
pass
async def observations(self):
"""Mock Observation."""
return OBS_METAR
async def forecast(self):
"""Mock Forecast."""
return FORE
async def forecast_hourly(self):
"""Mock Hourly Forecast."""
return HOURLY_FORE
async def stations(self):
"""Mock stations."""
return [STN]
class TestNWS_Metar(unittest.TestCase):
"""Test the NWS weather component."""
def setUp(self):
"""Set up things to be run when tests are started."""
self.hass = get_test_home_assistant()
self.hass.config.units = IMPERIAL_SYSTEM
self.lat = self.hass.config.latitude = 40.00
self.lon = self.hass.config.longitude = -8.00
def tearDown(self):
"""Stop down everything that was started."""
self.hass.stop()
@MockDependency("pynws")
@patch("pynws.Nws", new=MockNws_Metar)
def test_metar(self, mock_pynws):
"""Test for successfully setting up the NWS platform with name."""
assert setup_component(self.hass, weather.DOMAIN, {
'weather': {
'name': 'HomeWeather',
'platform': 'nws',
'api_key': 'test_email',
}
})
from metar import Metar
truth = Metar.Metar(METAR_MSG)
state = self.hass.states.get('weather.homeweather')
data = state.attributes
temp_f = convert_temperature(truth.temp.value(), TEMP_CELSIUS,
TEMP_FAHRENHEIT)
assert data.get(ATTR_WEATHER_TEMPERATURE) == \
display_temp(self.hass, temp_f, TEMP_FAHRENHEIT, PRECISION_WHOLE)
assert data.get(ATTR_WEATHER_HUMIDITY) is None
assert data.get(ATTR_WEATHER_PRESSURE) == round(
convert_pressure(truth.press.value(units='HPA'), PRESSURE_HPA,
PRESSURE_INHG),
2)
assert data.get(ATTR_WEATHER_WIND_SPEED) == round(
truth.wind_speed.value(units='MPH'))
assert data.get(ATTR_WEATHER_WIND_BEARING) == truth.wind_dir.value()
assert data.get(ATTR_WEATHER_VISIBILITY) == round(
truth.vis.value(units='MI'))