mirror of
https://github.com/home-assistant/core.git
synced 2025-07-31 19:25:12 +02:00
change to simpler api
This commit is contained in:
@@ -4,8 +4,5 @@
|
||||
"documentation": "https://www.home-assistant.io/components/nws",
|
||||
"dependencies": [],
|
||||
"codeowners": ["@MatthewFlamm"],
|
||||
"requirements": [
|
||||
"pynws==0.6",
|
||||
"metar==1.7.0"
|
||||
]
|
||||
"requirements": ["pynws==0.7.0"]
|
||||
}
|
||||
|
@@ -1,13 +1,10 @@
|
||||
"""Support for NWS weather service."""
|
||||
import asyncio
|
||||
from collections import OrderedDict
|
||||
from datetime import timedelta
|
||||
from json import JSONDecodeError
|
||||
import logging
|
||||
from statistics import mean
|
||||
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
@@ -72,13 +69,7 @@ CONDITION_CLASSES = OrderedDict([
|
||||
'Partly cloudy']),
|
||||
])
|
||||
|
||||
ERRORS = (aiohttp.ClientError, JSONDecodeError, asyncio.CancelledError)
|
||||
|
||||
FORECAST_CLASSES = {
|
||||
ATTR_FORECAST_DETAIL_DESCRIPTION: 'detailedForecast',
|
||||
ATTR_FORECAST_TEMP: 'temperature',
|
||||
ATTR_FORECAST_TIME: 'startTime',
|
||||
}
|
||||
ERRORS = (aiohttp.ClientError, JSONDecodeError)
|
||||
|
||||
FORECAST_MODE = ['daynight', 'hourly']
|
||||
|
||||
@@ -88,7 +79,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_LONGITUDE): cv.longitude,
|
||||
vol.Optional(CONF_MODE, default='daynight'): vol.In(FORECAST_MODE),
|
||||
vol.Optional(CONF_STATION): cv.string,
|
||||
vol.Required(CONF_API_KEY): cv.string
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
})
|
||||
|
||||
|
||||
@@ -109,16 +100,16 @@ def convert_condition(time, weather):
|
||||
|
||||
if cond == 'clear':
|
||||
if time == 'day':
|
||||
return 'sunny', max(prec_prob)
|
||||
return 'sunny', max(prec_probs)
|
||||
if time == 'night':
|
||||
return 'clear-night', max(prec_prob)
|
||||
return cond, max(prec_prob)
|
||||
return 'clear-night', max(prec_probs)
|
||||
return cond, max(prec_probs)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the NWS weather platform."""
|
||||
from pynws import SimpleNws
|
||||
from pynws import SimpleNWS
|
||||
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
||||
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
||||
station = config.get(CONF_STATION)
|
||||
@@ -132,7 +123,7 @@ async def async_setup_platform(hass, config, async_add_entities,
|
||||
websession = async_get_clientsession(hass)
|
||||
# ID request as being from HA, pynws prepends the api_key in addition
|
||||
api_key_ha = f"{api_key} homeassistant"
|
||||
nws = simple_nws(lat, lon, userid=api_key_ha, mode, websession)
|
||||
nws = SimpleNWS(latitude, longitude, api_key_ha, mode, websession)
|
||||
|
||||
_LOGGER.debug("Setting up station: %s", station)
|
||||
try:
|
||||
@@ -147,25 +138,29 @@ async def async_setup_platform(hass, config, async_add_entities,
|
||||
latitude, longitude, nws.station)
|
||||
|
||||
async_add_entities(
|
||||
[NWSWeather(nws, hass.config.units, config)],
|
||||
[NWSWeather(nws, mode, hass.config.units, config)],
|
||||
True)
|
||||
|
||||
|
||||
class NWSWeather(WeatherEntity):
|
||||
"""Representation of a weather condition."""
|
||||
|
||||
def __init__(self, nws, units, config):
|
||||
def __init__(self, nws, mode, units, config):
|
||||
"""Initialise the platform with a data instance and station name."""
|
||||
self.nws = nws
|
||||
self.station_name = config.get(CONF_NAME, self._nws.station)
|
||||
self.station_name = config.get(CONF_NAME, self.nws.station)
|
||||
self.is_metric = units.is_metric
|
||||
self.mode = mode
|
||||
|
||||
self.observation = None
|
||||
self._forecast = None
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
async def async_update(self):
|
||||
"""Update Condition."""
|
||||
_LOGGER.debug("Updating station observations %s", self.nws.station)
|
||||
try:
|
||||
await self.nws.update_observations()
|
||||
await self.nws.update_observation()
|
||||
except ERRORS as status:
|
||||
_LOGGER.error("Error updating observation from station %s: %s",
|
||||
self.nws.station, status)
|
||||
@@ -178,7 +173,7 @@ class NWSWeather(WeatherEntity):
|
||||
_LOGGER.error("Error updating forecast from station %s: %s",
|
||||
self.nws.station, status)
|
||||
else:
|
||||
self.forecast = self.nws.forecast
|
||||
self._forecast = self.nws.forecast
|
||||
return
|
||||
|
||||
@property
|
||||
@@ -205,7 +200,7 @@ class NWSWeather(WeatherEntity):
|
||||
def pressure(self):
|
||||
"""Return the current pressure."""
|
||||
pressure_pa = None
|
||||
if self._observation:
|
||||
if self.observation:
|
||||
pressure_pa = self.observation.get('seaLevelPressure')
|
||||
if pressure_pa is None:
|
||||
return None
|
||||
@@ -237,7 +232,7 @@ class NWSWeather(WeatherEntity):
|
||||
return None
|
||||
wind_m_hr = wind_m_s * 3600
|
||||
|
||||
if self._is_metric:
|
||||
if self.is_metric:
|
||||
wind = convert_distance(wind_m_hr,
|
||||
LENGTH_METERS, LENGTH_KILOMETERS)
|
||||
else:
|
||||
@@ -275,11 +270,11 @@ class NWSWeather(WeatherEntity):
|
||||
"""Return visibility."""
|
||||
vis_m = None
|
||||
if self.observation:
|
||||
vis_m = self._observation.get('visibility')
|
||||
vis_m = self.observation.get('visibility')
|
||||
if vis_m is None:
|
||||
return None
|
||||
|
||||
if self._is_metric:
|
||||
if self.is_metric:
|
||||
vis = convert_distance(vis_m, LENGTH_METERS, LENGTH_KILOMETERS)
|
||||
else:
|
||||
vis = convert_distance(vis_m, LENGTH_METERS, LENGTH_MILES)
|
||||
@@ -288,31 +283,39 @@ class NWSWeather(WeatherEntity):
|
||||
@property
|
||||
def forecast(self):
|
||||
"""Return forecast."""
|
||||
if self._forecast is None:
|
||||
return None
|
||||
forecast = []
|
||||
for forecast_entry in self._forecast:
|
||||
data = {
|
||||
ATTR_FORECAST_DETAIL_DESCRIPTION: forecast_entry.get('detailedForecast'),
|
||||
ATTR_FORECAST_DETAIL_DESCRIPTION: forecast_entry.get(
|
||||
'detailedForecast'),
|
||||
ATTR_FORECAST_TEMP: forecast_entry.get('temperature'),
|
||||
ATTR_FORECAST_TIME: forecast_entry.get('startTime'),
|
||||
}
|
||||
|
||||
if self._mode == 'daynight':
|
||||
|
||||
if self.mode == 'daynight':
|
||||
data[ATTR_FORECAST_DAYTIME] = forecast_entry.get('isDaytime')
|
||||
time = forecast_entry.get('iconTime')
|
||||
weather = forecast_entry.get('iconWeather')
|
||||
cond, precip = convert_condition(time, weather)
|
||||
if time and weather:
|
||||
cond, precip = convert_condition(time, weather)
|
||||
else:
|
||||
cond, precip = None, None
|
||||
data[ATTR_FORECAST_CONDITION] = cond
|
||||
data[ATTR_FORECAST_PRECIP_PROB] = precip
|
||||
|
||||
data[ATTR_FORECAST_WIND_BEARING] = \
|
||||
WIND[forecast_entry['windDirection']]
|
||||
forecast_entry.get('windBearing')
|
||||
wind_speed = forecast_entry.get('windSpeedAvg')
|
||||
if self._is_metric:
|
||||
data[ATTR_FORECAST_WIND_SPEED] = round(
|
||||
convert_distance(wind_speed,
|
||||
LENGTH_MILES, LENGTH_KILOMETERS))
|
||||
if wind_speed:
|
||||
if self.is_metric:
|
||||
data[ATTR_FORECAST_WIND_SPEED] = round(
|
||||
convert_distance(wind_speed,
|
||||
LENGTH_MILES, LENGTH_KILOMETERS))
|
||||
else:
|
||||
data[ATTR_FORECAST_WIND_SPEED] = round(wind_speed)
|
||||
else:
|
||||
data[ATTR_FORECAST_WIND_SPEED] = round(wind_speed_avg)
|
||||
|
||||
data[ATTR_FORECAST_WIND_SPEED] = None
|
||||
forecast.append(data)
|
||||
return forecast
|
||||
|
@@ -765,9 +765,6 @@ 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
|
||||
|
||||
@@ -1292,7 +1289,7 @@ pynuki==1.3.3
|
||||
pynut2==2.1.2
|
||||
|
||||
# homeassistant.components.nws
|
||||
pynws==0.6
|
||||
pynws==0.7.0
|
||||
|
||||
# homeassistant.components.nx584
|
||||
pynx584==0.4
|
||||
|
@@ -26,129 +26,74 @@ from homeassistant.setup import setup_component
|
||||
|
||||
from tests.common import get_test_home_assistant, MockDependency
|
||||
|
||||
|
||||
OBS = [{
|
||||
'temperature': {'value': 7, 'qualityControl': 'qc:V'},
|
||||
'relativeHumidity': {'value': 10, 'qualityControl': 'qc:V'},
|
||||
'windChill': {'value': 10, 'qualityControl': 'qc:V'},
|
||||
'heatIndex': {'value': 10, 'qualityControl': 'qc:V'},
|
||||
'windDirection': {'value': 180, 'qualityControl': 'qc:V'},
|
||||
'visibility': {'value': 10000, 'qualityControl': 'qc:V'},
|
||||
'windSpeed': {'value': 10, 'qualityControl': 'qc:V'},
|
||||
'seaLevelPressure': {'value': 30000, 'qualityControl': 'qc:V'},
|
||||
'windGust': {'value': 10, 'qualityControl': 'qc:V'},
|
||||
'dewpoint': {'value': 10, 'qualityControl': 'qc:V'},
|
||||
'icon': 'https://api.weather.gov/icons/land/day/skc?size=medium',
|
||||
'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"},
|
||||
}]
|
||||
|
||||
OBS_NONE = [{
|
||||
"rawMessage": None,
|
||||
"textDescription": None,
|
||||
"icon": None,
|
||||
"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"},
|
||||
}]
|
||||
|
||||
OBS = {
|
||||
'temperature': 7,
|
||||
'relativeHumidity': 10,
|
||||
'windBearing': 180,
|
||||
'visibility': 10000,
|
||||
'windSpeed': 10,
|
||||
'seaLevelPressure': 30000,
|
||||
'iconTime': 'day',
|
||||
'iconWeather': (('Fair/clear', None),),
|
||||
}
|
||||
|
||||
FORE = [{
|
||||
'endTime': '2018-12-21T18:00:00-05:00',
|
||||
'windSpeed': '8 to 10 mph',
|
||||
'windDirection': 'S',
|
||||
'shortForecast': 'Chance Showers And Thunderstorms',
|
||||
'isDaytime': True,
|
||||
'startTime': '2018-12-21T15:00:00-05:00',
|
||||
'temperatureTrend': None,
|
||||
'temperature': 41,
|
||||
'temperatureUnit': 'F',
|
||||
'detailedForecast': 'A detailed description',
|
||||
'name': 'This Afternoon',
|
||||
'number': 1,
|
||||
'icon': 'https://api.weather.gov/icons/land/day/skc/tsra,40?size=medium'
|
||||
}]
|
||||
|
||||
HOURLY_FORE = [{
|
||||
'endTime': '2018-12-22T05:00:00-05:00',
|
||||
'windSpeed': '4 mph',
|
||||
'windDirection': 'N',
|
||||
'shortForecast': 'Chance Showers And Thunderstorms',
|
||||
'startTime': '2018-12-22T04:00:00-05:00',
|
||||
'temperatureTrend': None,
|
||||
'temperature': 32,
|
||||
'temperatureUnit': 'F',
|
||||
'detailedForecast': '',
|
||||
'number': 2,
|
||||
'icon': 'https://api.weather.gov/icons/land/night/skc?size=medium'
|
||||
'windBearing': 180,
|
||||
'windSpeedAvg': 9,
|
||||
'iconTime': 'day',
|
||||
'startTime': '2018-12-21T15:00:00-05:00',
|
||||
'iconWeather': (('Fair/Clear', None),
|
||||
('Thunderstorm (high cloud cover)', 40),),
|
||||
}]
|
||||
|
||||
STN = 'STNA'
|
||||
|
||||
|
||||
class MockNws():
|
||||
class MockNws:
|
||||
"""Mock Station from pynws."""
|
||||
|
||||
def __init__(self, websession, latlon, userid):
|
||||
data_obs = None
|
||||
data_fore = None
|
||||
|
||||
error_obs = False
|
||||
error_fore = False
|
||||
error_stn = False
|
||||
|
||||
def __init__(self, lat, lon, userid, mode, session):
|
||||
"""Init mock nws."""
|
||||
pass
|
||||
self.station = None
|
||||
self.stations = None
|
||||
|
||||
async def observations(self, limit):
|
||||
async def update_observation(self):
|
||||
"""Mock observation update."""
|
||||
if self.error_obs:
|
||||
raise aiohttp.ClientError
|
||||
return
|
||||
|
||||
async def update_forecast(self):
|
||||
"""Mock forecast update."""
|
||||
if self.error_fore:
|
||||
raise aiohttp.ClientError
|
||||
return
|
||||
|
||||
@property
|
||||
def observation(self):
|
||||
"""Mock Observation."""
|
||||
return OBS
|
||||
return self.data_obs
|
||||
|
||||
async def forecast(self):
|
||||
@property
|
||||
def forecast(self):
|
||||
"""Mock Forecast."""
|
||||
return FORE
|
||||
return self.data_fore
|
||||
|
||||
async def forecast_hourly(self):
|
||||
"""Mock Hourly Forecast."""
|
||||
return HOURLY_FORE
|
||||
|
||||
async def stations(self):
|
||||
async def set_station(self, station=None):
|
||||
"""Mock stations."""
|
||||
return [STN]
|
||||
|
||||
|
||||
class Prop:
|
||||
"""Property data class for metar. Initialize with desired return value."""
|
||||
|
||||
def __init__(self, value_return):
|
||||
"""Initialize with desired return."""
|
||||
self.value_return = value_return
|
||||
|
||||
def value(self, units=''):
|
||||
"""Return provided value."""
|
||||
return self.value_return
|
||||
|
||||
|
||||
class MockMetar:
|
||||
"""Mock Metar parser."""
|
||||
|
||||
def __init__(self, code):
|
||||
"""Set up mocked return values."""
|
||||
self.temp = Prop(27)
|
||||
self.press = Prop(1111)
|
||||
self.wind_speed = Prop(27)
|
||||
self.wind_dir = Prop(175)
|
||||
self.vis = Prop(5000)
|
||||
if self.error_stn:
|
||||
raise aiohttp.ClientError
|
||||
self.stations = [STN]
|
||||
self.station = station or STN
|
||||
return
|
||||
|
||||
|
||||
class TestNWS(unittest.TestCase):
|
||||
@@ -161,15 +106,25 @@ class TestNWS(unittest.TestCase):
|
||||
self.lat = self.hass.config.latitude = 40.00
|
||||
self.lon = self.hass.config.longitude = -8.00
|
||||
|
||||
# Initialize class variables as tests modify them
|
||||
MockNws.data_obs = None
|
||||
MockNws.data_fore = None
|
||||
|
||||
MockNws.error_obs = False
|
||||
MockNws.error_fore = False
|
||||
MockNws.error_stn = False
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop down everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_w_name(self, mock_metar, mock_pynws):
|
||||
"""Test for successfully setting up the NWS platform with name."""
|
||||
@MockDependency('pynws')
|
||||
@patch("pynws.SimpleNWS", new=MockNws)
|
||||
def test_nws(self, mock_pynws):
|
||||
"""Test for successfully setting up with imperial."""
|
||||
mock_pynws.SimpleNWS.data_obs = OBS
|
||||
mock_pynws.SimpleNWS.data_fore = FORE
|
||||
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'name': 'HomeWeather',
|
||||
@@ -203,137 +158,14 @@ class TestNWS(unittest.TestCase):
|
||||
assert forecast[0].get(ATTR_FORECAST_WIND_BEARING) == 180
|
||||
assert forecast[0].get(ATTR_FORECAST_WIND_SPEED) == 9
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_w_station(self, mock_metar, mock_pynws):
|
||||
"""Test for successfully setting up the NWS platform with station."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'platform': 'nws',
|
||||
'station': 'STNB',
|
||||
'api_key': 'test_email',
|
||||
}
|
||||
})
|
||||
@MockDependency('pynws')
|
||||
@patch("pynws.SimpleNWS", new=MockNws)
|
||||
def test_nws_metric(self, mock_pynws):
|
||||
"""Test for successfully setting up with metric."""
|
||||
mock_pynws.SimpleNWS.data_obs = OBS
|
||||
mock_pynws.SimpleNWS.data_fore = FORE
|
||||
|
||||
assert self.hass.states.get('weather.stnb')
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_w_no_name(self, mock_metar, mock_pynws):
|
||||
"""Test for successfully setting up the NWS platform w no name."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'platform': 'nws',
|
||||
'api_key': 'test_email',
|
||||
}
|
||||
})
|
||||
|
||||
assert self.hass.states.get('weather.' + STN)
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test__hourly(self, mock_metar, mock_pynws):
|
||||
"""Test for successfully setting up hourly forecast."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'name': 'HourlyWeather',
|
||||
'platform': 'nws',
|
||||
'api_key': 'test_email',
|
||||
'mode': 'hourly',
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('weather.hourlyweather')
|
||||
data = state.attributes
|
||||
|
||||
forecast = data.get(ATTR_FORECAST)
|
||||
assert forecast[0].get(ATTR_FORECAST_CONDITION) == 'clear-night'
|
||||
assert forecast[0].get(ATTR_FORECAST_PRECIP_PROB) is None
|
||||
assert forecast[0].get(ATTR_FORECAST_TEMP) == 32
|
||||
assert forecast[0].get(ATTR_FORECAST_TIME) == \
|
||||
'2018-12-22T04:00:00-05:00'
|
||||
assert forecast[0].get(ATTR_FORECAST_WIND_BEARING) == 0
|
||||
assert forecast[0].get(ATTR_FORECAST_WIND_SPEED) == 4
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_daynight(self, mock_metar, mock_pynws):
|
||||
"""Test for successfully setting up daynight forecast."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'platform': 'nws',
|
||||
'api_key': 'test_email',
|
||||
'mode': 'daynight',
|
||||
}
|
||||
})
|
||||
assert self.hass.states.get('weather.' + STN)
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_latlon(self, mock_metar, mock_pynws):
|
||||
"""Test for successsfully setting up the NWS platform with lat/lon."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'platform': 'nws',
|
||||
'api_key': 'test_email',
|
||||
'latitude': self.lat,
|
||||
'longitude': self.lon,
|
||||
}
|
||||
})
|
||||
assert self.hass.states.get('weather.' + STN)
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_setup_failure_mode(self, mock_metar, mock_pynws):
|
||||
"""Test for unsuccessfully setting up incorrect mode."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'platform': 'nws',
|
||||
'api_key': 'test_email',
|
||||
'mode': 'abc',
|
||||
}
|
||||
})
|
||||
assert self.hass.states.get('weather.' + STN) is None
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_setup_failure_no_apikey(self, mock_metar, mock_pynws):
|
||||
"""Test for unsuccessfully setting up without api_key."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'platform': 'nws',
|
||||
}
|
||||
})
|
||||
|
||||
assert self.hass.states.get('weather.' + STN) is None
|
||||
|
||||
|
||||
class TestNwsMetric(unittest.TestCase):
|
||||
"""Test the NWS weather component using metric units."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.units = METRIC_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("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_metric(self, mock_metar, mock_pynws):
|
||||
"""Test for successfully setting up the NWS platform with name."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'name': 'HomeWeather',
|
||||
@@ -346,14 +178,13 @@ class TestNwsMetric(unittest.TestCase):
|
||||
assert state.state == 'sunny'
|
||||
|
||||
data = state.attributes
|
||||
temp_f = convert_temperature(7, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
assert data.get(ATTR_WEATHER_TEMPERATURE) == \
|
||||
display_temp(self.hass, 7, TEMP_CELSIUS, PRECISION_WHOLE)
|
||||
|
||||
display_temp(self.hass, temp_f, TEMP_FAHRENHEIT, PRECISION_WHOLE)
|
||||
assert data.get(ATTR_WEATHER_HUMIDITY) == 10
|
||||
assert data.get(ATTR_WEATHER_PRESSURE) == round(
|
||||
convert_pressure(30000, PRESSURE_PA, PRESSURE_HPA))
|
||||
# m/s to km/hr
|
||||
assert data.get(ATTR_WEATHER_WIND_SPEED) == round(10 * 3.6)
|
||||
assert data.get(ATTR_WEATHER_WIND_SPEED) == round(3.6 * 10)
|
||||
assert data.get(ATTR_WEATHER_WIND_BEARING) == 180
|
||||
assert data.get(ATTR_WEATHER_VISIBILITY) == round(
|
||||
convert_distance(10000, LENGTH_METERS, LENGTH_KILOMETERS))
|
||||
@@ -362,47 +193,18 @@ class TestNwsMetric(unittest.TestCase):
|
||||
forecast = data.get(ATTR_FORECAST)
|
||||
assert forecast[0].get(ATTR_FORECAST_CONDITION) == 'lightning-rainy'
|
||||
assert forecast[0].get(ATTR_FORECAST_PRECIP_PROB) == 40
|
||||
assert forecast[0].get(ATTR_FORECAST_TEMP) == round(
|
||||
convert_temperature(41, TEMP_FAHRENHEIT, TEMP_CELSIUS))
|
||||
assert forecast[0].get(ATTR_FORECAST_TEMP) == convert_temperature(
|
||||
41, TEMP_FAHRENHEIT, TEMP_CELSIUS)
|
||||
assert forecast[0].get(ATTR_FORECAST_TIME) == \
|
||||
'2018-12-21T15:00:00-05:00'
|
||||
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(MockNws):
|
||||
"""Mock Station from pynws."""
|
||||
|
||||
def __init__(self, websession, latlon, userid):
|
||||
"""Init mock nws."""
|
||||
pass
|
||||
|
||||
async def observations(self, limit):
|
||||
"""Mock Observation."""
|
||||
return OBS_METAR
|
||||
|
||||
|
||||
class TestNWS_Metar(unittest.TestCase):
|
||||
"""Test the NWS weather component with metar code."""
|
||||
|
||||
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("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws_Metar)
|
||||
@patch("metar.Metar.Metar", new=MockMetar)
|
||||
def test_metar(self, mock_metar, mock_pynws):
|
||||
"""Test for successfully setting up the NWS platform with name."""
|
||||
@MockDependency('pynws')
|
||||
@patch("pynws.SimpleNWS", new=MockNws)
|
||||
def test_nws_no_obs_fore1x(self, mock_pynws):
|
||||
"""Test with no data."""
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'name': 'HomeWeather',
|
||||
@@ -411,154 +213,15 @@ class TestNWS_Metar(unittest.TestCase):
|
||||
}
|
||||
})
|
||||
|
||||
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(), PRESSURE_HPA, PRESSURE_INHG),
|
||||
2)
|
||||
|
||||
wind_speed_mi_s = convert_distance(
|
||||
truth.wind_speed.value(), LENGTH_METERS, LENGTH_MILES)
|
||||
assert data.get(ATTR_WEATHER_WIND_SPEED) == round(
|
||||
wind_speed_mi_s * 3600)
|
||||
assert data.get(ATTR_WEATHER_WIND_BEARING) == truth.wind_dir.value()
|
||||
vis = convert_distance(truth.vis.value(), LENGTH_METERS, LENGTH_MILES)
|
||||
assert data.get(ATTR_WEATHER_VISIBILITY) == round(vis)
|
||||
|
||||
|
||||
class MockNwsFailObs(MockNws):
|
||||
"""Mock Station from pynws."""
|
||||
|
||||
def __init__(self, websession, latlon, userid):
|
||||
"""Init mock nws."""
|
||||
pass
|
||||
|
||||
async def observations(self, limit):
|
||||
"""Mock Observation."""
|
||||
raise aiohttp.ClientError
|
||||
|
||||
|
||||
class MockNwsFailStn(MockNws):
|
||||
"""Mock Station from pynws."""
|
||||
|
||||
def __init__(self, websession, latlon, userid):
|
||||
"""Init mock nws."""
|
||||
pass
|
||||
|
||||
async def stations(self):
|
||||
"""Mock Observation."""
|
||||
raise aiohttp.ClientError
|
||||
|
||||
|
||||
class MockNwsFailFore(MockNws):
|
||||
"""Mock Station from pynws."""
|
||||
|
||||
def __init__(self, websession, latlon, userid):
|
||||
"""Init mock nws."""
|
||||
pass
|
||||
|
||||
async def forecast(self):
|
||||
"""Mock Observation."""
|
||||
raise aiohttp.ClientError
|
||||
|
||||
|
||||
class MockNws_NoObs(MockNws):
|
||||
"""Mock Station from pynws."""
|
||||
|
||||
def __init__(self, websession, latlon, userid):
|
||||
"""Init mock nws."""
|
||||
pass
|
||||
|
||||
async def observations(self, limit):
|
||||
"""Mock Observation."""
|
||||
return OBS_NONE
|
||||
|
||||
|
||||
class TestFailures(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("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNwsFailObs)
|
||||
def test_obs_fail(self, mock_metar, 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',
|
||||
}
|
||||
})
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNwsFailStn)
|
||||
def test_fail_stn(self, mock_metar, 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',
|
||||
}
|
||||
})
|
||||
state = self.hass.states.get('weather.homeweather')
|
||||
assert state is None
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNwsFailFore)
|
||||
def test_fail_fore(self, mock_metar, 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',
|
||||
}
|
||||
})
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws_NoObs)
|
||||
def test_no_obs(self, mock_metar, 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',
|
||||
}
|
||||
})
|
||||
state = self.hass.states.get('weather.homeweather')
|
||||
assert state.state == 'unknown'
|
||||
|
||||
@MockDependency("metar")
|
||||
@MockDependency("pynws")
|
||||
@patch("pynws.Nws", new=MockNws)
|
||||
def test_no_lat(self, mock_metar, mock_pynws):
|
||||
"""Test for successfully setting up the NWS platform with name."""
|
||||
hass = self.hass
|
||||
hass.config.latitude = None
|
||||
@MockDependency('pynws')
|
||||
@patch("pynws.SimpleNWS", new=MockNws)
|
||||
def test_nws_missing_valuess(self, mock_pynws):
|
||||
"""Test with missing data."""
|
||||
mock_pynws.SimpleNWS.data_obs = {key: None for key in OBS}
|
||||
mock_pynws.SimpleNWS.data_fore = [{key: None for key in FORE[0]}]
|
||||
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
@@ -568,5 +231,55 @@ class TestFailures(unittest.TestCase):
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('weather.homeweather')
|
||||
assert state.state == 'unknown'
|
||||
|
||||
@MockDependency('pynws')
|
||||
@patch("pynws.SimpleNWS", new=MockNws)
|
||||
def test_nws_error_obs(self, mock_pynws):
|
||||
"""Test for successfully setting up the NWS platform with name."""
|
||||
mock_pynws.SimpleNWS.error_obs = True
|
||||
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'name': 'HomeWeather',
|
||||
'platform': 'nws',
|
||||
'api_key': 'test_email',
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('weather.homeweather')
|
||||
assert state.state == 'unknown'
|
||||
|
||||
@MockDependency('pynws')
|
||||
@patch("pynws.SimpleNWS", new=MockNws)
|
||||
def test_nws_error_fore(self, mock_pynws):
|
||||
"""Test error forecast."""
|
||||
mock_pynws.SimpleNWS.error_fore = True
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'name': 'HomeWeather',
|
||||
'platform': 'nws',
|
||||
'api_key': 'test_email',
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('weather.homeweather')
|
||||
data = state.attributes
|
||||
assert data.get('forecast') is None
|
||||
|
||||
@MockDependency('pynws')
|
||||
@patch("pynws.SimpleNWS", new=MockNws)
|
||||
def test_nws_error_stn(self, mock_pynws):
|
||||
"""Test station error.."""
|
||||
mock_pynws.SimpleNWS.error_stn = True
|
||||
assert setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'name': 'HomeWeather',
|
||||
'platform': 'nws',
|
||||
'api_key': 'test_email',
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('weather.homeweather')
|
||||
assert state is None
|
||||
|
Reference in New Issue
Block a user