mirror of
https://github.com/home-assistant/core.git
synced 2025-08-15 02:21:40 +02:00
Add metar fallback.
Use metar code from nws observation if normal api is missing data.
This commit is contained in:
@@ -4,5 +4,8 @@
|
|||||||
"documentation": "https://www.home-assistant.io/components/nws",
|
"documentation": "https://www.home-assistant.io/components/nws",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@MatthewFlamm"],
|
"codeowners": ["@MatthewFlamm"],
|
||||||
"requirements": ["pynws==0.6"]
|
"requirements": [
|
||||||
|
"pynws==0.6",
|
||||||
|
"metar==1.7.0"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@@ -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.pressure import convert as convert_pressure
|
||||||
from homeassistant.util.temperature import convert as convert_temperature
|
from homeassistant.util.temperature import convert as convert_temperature
|
||||||
|
|
||||||
REQUIREMENTS = ['pynws==0.6']
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ATTRIBUTION = 'Data from National Weather Service/NOAA'
|
ATTRIBUTION = 'Data from National Weather Service/NOAA'
|
||||||
@@ -127,7 +125,7 @@ async def async_setup_platform(hass, config, async_add_entities,
|
|||||||
discovery_info=None):
|
discovery_info=None):
|
||||||
"""Set up the NWS weather platform."""
|
"""Set up the NWS weather platform."""
|
||||||
from pynws import Nws
|
from pynws import Nws
|
||||||
|
from metar import Metar
|
||||||
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
||||||
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
||||||
station = config.get(CONF_STATION)
|
station = config.get(CONF_STATION)
|
||||||
@@ -155,16 +153,21 @@ async def async_setup_platform(hass, config, async_add_entities,
|
|||||||
nws.station = station
|
nws.station = station
|
||||||
_LOGGER.debug("Initialized station %s", station[0])
|
_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):
|
class NWSWeather(WeatherEntity):
|
||||||
"""Representation of a weather condition."""
|
"""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."""
|
"""Initialise the platform with a data instance and station name."""
|
||||||
self._nws = nws
|
self._nws = nws
|
||||||
|
self._metar = metar
|
||||||
self._station_name = config.get(CONF_NAME, self._nws.station)
|
self._station_name = config.get(CONF_NAME, self._nws.station)
|
||||||
|
|
||||||
|
self._metar_obs = None
|
||||||
self._observation = None
|
self._observation = None
|
||||||
self._forecast = None
|
self._forecast = None
|
||||||
self._description = None
|
self._description = None
|
||||||
@@ -178,6 +181,11 @@ class NWSWeather(WeatherEntity):
|
|||||||
_LOGGER.debug("Updating station observations %s",
|
_LOGGER.debug("Updating station observations %s",
|
||||||
self._nws.station)
|
self._nws.station)
|
||||||
self._observation = await self._nws.observations()
|
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")
|
_LOGGER.debug("Updating forecast")
|
||||||
if self._mode == 'daynight':
|
if self._mode == 'daynight':
|
||||||
self._forecast = await self._nws.forecast()
|
self._forecast = await self._nws.forecast()
|
||||||
@@ -200,6 +208,8 @@ class NWSWeather(WeatherEntity):
|
|||||||
def temperature(self):
|
def temperature(self):
|
||||||
"""Return the current temperature."""
|
"""Return the current temperature."""
|
||||||
temp_c = self._observation[0]['temperature']['value']
|
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:
|
if temp_c is not None:
|
||||||
return convert_temperature(temp_c, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
return convert_temperature(temp_c, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||||
return None
|
return None
|
||||||
@@ -208,9 +218,13 @@ class NWSWeather(WeatherEntity):
|
|||||||
def pressure(self):
|
def pressure(self):
|
||||||
"""Return the current pressure."""
|
"""Return the current pressure."""
|
||||||
pressure_pa = self._observation[0]['seaLevelPressure']['value']
|
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
|
return None
|
||||||
|
pressure_pa = convert_pressure(pressure_hpa, PRESSURE_HPA,
|
||||||
|
PRESSURE_PA)
|
||||||
|
|
||||||
if self._is_metric:
|
if self._is_metric:
|
||||||
pressure = convert_pressure(pressure_pa, PRESSURE_PA, PRESSURE_HPA)
|
pressure = convert_pressure(pressure_pa, PRESSURE_PA, PRESSURE_HPA)
|
||||||
@@ -230,6 +244,9 @@ class NWSWeather(WeatherEntity):
|
|||||||
def wind_speed(self):
|
def wind_speed(self):
|
||||||
"""Return the current windspeed."""
|
"""Return the current windspeed."""
|
||||||
wind_m_s = self._observation[0]['windSpeed']['value']
|
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:
|
if wind_m_s is None:
|
||||||
return None
|
return None
|
||||||
wind_m_hr = wind_m_s * 3600
|
wind_m_hr = wind_m_s * 3600
|
||||||
@@ -244,7 +261,10 @@ class NWSWeather(WeatherEntity):
|
|||||||
@property
|
@property
|
||||||
def wind_bearing(self):
|
def wind_bearing(self):
|
||||||
"""Return the current wind bearing (degrees)."""
|
"""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
|
@property
|
||||||
def temperature_unit(self):
|
def temperature_unit(self):
|
||||||
@@ -262,6 +282,8 @@ class NWSWeather(WeatherEntity):
|
|||||||
def visibility(self):
|
def visibility(self):
|
||||||
"""Return visibility."""
|
"""Return visibility."""
|
||||||
vis_m = self._observation[0]['visibility']['value']
|
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:
|
if vis_m is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@@ -765,6 +765,9 @@ mbddns==0.1.2
|
|||||||
# homeassistant.components.message_bird
|
# homeassistant.components.message_bird
|
||||||
messagebird==1.2.0
|
messagebird==1.2.0
|
||||||
|
|
||||||
|
# homeassistant.components.nws
|
||||||
|
metar==1.7.0
|
||||||
|
|
||||||
# homeassistant.components.meteoalarm
|
# homeassistant.components.meteoalarm
|
||||||
meteoalertapi==0.1.5
|
meteoalertapi==0.1.5
|
||||||
|
|
||||||
|
@@ -40,6 +40,21 @@ OBS = [{
|
|||||||
'textDescription': 'Sunny'
|
'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 = [{
|
FORE = [{
|
||||||
'endTime': '2018-12-21T18:00:00-05:00',
|
'endTime': '2018-12-21T18:00:00-05:00',
|
||||||
'windSpeed': '8 to 10 mph',
|
'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_BEARING) == 180
|
||||||
assert forecast[0].get(ATTR_FORECAST_WIND_SPEED) == round(
|
assert forecast[0].get(ATTR_FORECAST_WIND_SPEED) == round(
|
||||||
convert_distance(9, LENGTH_MILES, LENGTH_KILOMETERS))
|
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'))
|
||||||
|
Reference in New Issue
Block a user