mirror of
https://github.com/home-assistant/core
synced 2024-08-02 23:40:32 +02:00
Add nws sensor platform (#45027)
* Resolve rebase conflict. Remove logging * lint: fix elif after return * fix attribution * add tests for None valuea * Remove Entity import Co-authored-by: Erik Montnemery <erik@montnemery.com> * Import SensorEntity Co-authored-by: Erik Montnemery <erik@montnemery.com> * Inherit SensorEntity Co-authored-by: Erik Montnemery <erik@montnemery.com> * remove unused logging * Use CoordinatorEntity * Use type instead of name. * add all entities * add nice rounding to temperature and humidity Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
9f481e1642
commit
f8f0495319
@ -28,7 +28,7 @@ from .const import (
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = ["weather"]
|
||||
PLATFORMS = ["sensor", "weather"]
|
||||
|
||||
DEFAULT_SCAN_INTERVAL = datetime.timedelta(minutes=10)
|
||||
FAILED_SCAN_INTERVAL = datetime.timedelta(minutes=1)
|
||||
|
@ -1,4 +1,6 @@
|
||||
"""Constants for National Weather Service Integration."""
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
ATTR_CONDITION_EXCEPTIONAL,
|
||||
@ -14,6 +16,21 @@ from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_WINDY,
|
||||
ATTR_CONDITION_WINDY_VARIANT,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
DEGREE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
LENGTH_METERS,
|
||||
LENGTH_MILES,
|
||||
PERCENTAGE,
|
||||
PRESSURE_INHG,
|
||||
PRESSURE_PA,
|
||||
SPEED_KILOMETERS_PER_HOUR,
|
||||
SPEED_MILES_PER_HOUR,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
|
||||
DOMAIN = "nws"
|
||||
|
||||
@ -23,6 +40,11 @@ ATTRIBUTION = "Data from National Weather Service/NOAA"
|
||||
|
||||
ATTR_FORECAST_DETAILED_DESCRIPTION = "detailed_description"
|
||||
ATTR_FORECAST_DAYTIME = "daytime"
|
||||
ATTR_ICON = "icon"
|
||||
ATTR_LABEL = "label"
|
||||
ATTR_UNIT = "unit"
|
||||
ATTR_UNIT_CONVERT = "unit_convert"
|
||||
ATTR_UNIT_CONVERT_METHOD = "unit_convert_method"
|
||||
|
||||
CONDITION_CLASSES = {
|
||||
ATTR_CONDITION_EXCEPTIONAL: [
|
||||
@ -75,3 +97,86 @@ NWS_DATA = "nws data"
|
||||
COORDINATOR_OBSERVATION = "coordinator_observation"
|
||||
COORDINATOR_FORECAST = "coordinator_forecast"
|
||||
COORDINATOR_FORECAST_HOURLY = "coordinator_forecast_hourly"
|
||||
|
||||
OBSERVATION_VALID_TIME = timedelta(minutes=20)
|
||||
FORECAST_VALID_TIME = timedelta(minutes=45)
|
||||
|
||||
SENSOR_TYPES = {
|
||||
"dewpoint": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Dew Point",
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_UNIT_CONVERT: TEMP_CELSIUS,
|
||||
},
|
||||
"temperature": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Temperature",
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_UNIT_CONVERT: TEMP_CELSIUS,
|
||||
},
|
||||
"windChill": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Wind Chill",
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_UNIT_CONVERT: TEMP_CELSIUS,
|
||||
},
|
||||
"heatIndex": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Heat Index",
|
||||
ATTR_UNIT: TEMP_CELSIUS,
|
||||
ATTR_UNIT_CONVERT: TEMP_CELSIUS,
|
||||
},
|
||||
"relativeHumidity": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Relative Humidity",
|
||||
ATTR_UNIT: PERCENTAGE,
|
||||
ATTR_UNIT_CONVERT: PERCENTAGE,
|
||||
},
|
||||
"windSpeed": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-windy",
|
||||
ATTR_LABEL: "Wind Speed",
|
||||
ATTR_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||
ATTR_UNIT_CONVERT: SPEED_MILES_PER_HOUR,
|
||||
},
|
||||
"windGust": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-windy",
|
||||
ATTR_LABEL: "Wind Gust",
|
||||
ATTR_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||
ATTR_UNIT_CONVERT: SPEED_MILES_PER_HOUR,
|
||||
},
|
||||
"windDirection": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:compass-rose",
|
||||
ATTR_LABEL: "Wind Direction",
|
||||
ATTR_UNIT: DEGREE,
|
||||
ATTR_UNIT_CONVERT: DEGREE,
|
||||
},
|
||||
"barometricPressure": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PRESSURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Barometric Pressure",
|
||||
ATTR_UNIT: PRESSURE_PA,
|
||||
ATTR_UNIT_CONVERT: PRESSURE_INHG,
|
||||
},
|
||||
"seaLevelPressure": {
|
||||
ATTR_DEVICE_CLASS: DEVICE_CLASS_PRESSURE,
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "Sea Level Pressure",
|
||||
ATTR_UNIT: PRESSURE_PA,
|
||||
ATTR_UNIT_CONVERT: PRESSURE_INHG,
|
||||
},
|
||||
"visibility": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:eye",
|
||||
ATTR_LABEL: "Visibility",
|
||||
ATTR_UNIT: LENGTH_METERS,
|
||||
ATTR_UNIT_CONVERT: LENGTH_MILES,
|
||||
},
|
||||
}
|
||||
|
156
homeassistant/components/nws/sensor.py
Normal file
156
homeassistant/components/nws/sensor.py
Normal file
@ -0,0 +1,156 @@
|
||||
"""Sensors for National Weather Service (NWS)."""
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
ATTR_DEVICE_CLASS,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
LENGTH_KILOMETERS,
|
||||
LENGTH_METERS,
|
||||
LENGTH_MILES,
|
||||
PERCENTAGE,
|
||||
PRESSURE_INHG,
|
||||
PRESSURE_PA,
|
||||
SPEED_MILES_PER_HOUR,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util.distance import convert as convert_distance
|
||||
from homeassistant.util.dt import utcnow
|
||||
from homeassistant.util.pressure import convert as convert_pressure
|
||||
|
||||
from . import base_unique_id
|
||||
from .const import (
|
||||
ATTR_ICON,
|
||||
ATTR_LABEL,
|
||||
ATTR_UNIT,
|
||||
ATTR_UNIT_CONVERT,
|
||||
ATTRIBUTION,
|
||||
CONF_STATION,
|
||||
COORDINATOR_OBSERVATION,
|
||||
DOMAIN,
|
||||
NWS_DATA,
|
||||
OBSERVATION_VALID_TIME,
|
||||
SENSOR_TYPES,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up the NWS weather platform."""
|
||||
hass_data = hass.data[DOMAIN][entry.entry_id]
|
||||
station = entry.data[CONF_STATION]
|
||||
|
||||
entities = []
|
||||
for sensor_type, sensor_data in SENSOR_TYPES.items():
|
||||
if hass.config.units.is_metric:
|
||||
unit = sensor_data[ATTR_UNIT]
|
||||
else:
|
||||
unit = sensor_data[ATTR_UNIT_CONVERT]
|
||||
entities.append(
|
||||
NWSSensor(
|
||||
entry.data,
|
||||
hass_data,
|
||||
sensor_type,
|
||||
station,
|
||||
sensor_data[ATTR_LABEL],
|
||||
sensor_data[ATTR_ICON],
|
||||
sensor_data[ATTR_DEVICE_CLASS],
|
||||
unit,
|
||||
),
|
||||
)
|
||||
|
||||
async_add_entities(entities, False)
|
||||
|
||||
|
||||
class NWSSensor(CoordinatorEntity, SensorEntity):
|
||||
"""An NWS Sensor Entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
entry_data,
|
||||
hass_data,
|
||||
sensor_type,
|
||||
station,
|
||||
label,
|
||||
icon,
|
||||
device_class,
|
||||
unit,
|
||||
):
|
||||
"""Initialise the platform with a data instance."""
|
||||
super().__init__(hass_data[COORDINATOR_OBSERVATION])
|
||||
self._nws = hass_data[NWS_DATA]
|
||||
self._latitude = entry_data[CONF_LATITUDE]
|
||||
self._longitude = entry_data[CONF_LONGITUDE]
|
||||
self._type = sensor_type
|
||||
self._station = station
|
||||
self._label = label
|
||||
self._icon = icon
|
||||
self._device_class = device_class
|
||||
self._unit = unit
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state."""
|
||||
value = self._nws.observation.get(self._type)
|
||||
if value is None:
|
||||
return None
|
||||
if self._unit == SPEED_MILES_PER_HOUR:
|
||||
return round(convert_distance(value, LENGTH_KILOMETERS, LENGTH_MILES))
|
||||
if self._unit == LENGTH_MILES:
|
||||
return round(convert_distance(value, LENGTH_METERS, LENGTH_MILES))
|
||||
if self._unit == PRESSURE_INHG:
|
||||
return round(convert_pressure(value, PRESSURE_PA, PRESSURE_INHG), 2)
|
||||
if self._unit == TEMP_CELSIUS:
|
||||
return round(value, 1)
|
||||
if self._unit == PERCENTAGE:
|
||||
return round(value)
|
||||
return value
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon."""
|
||||
return self._icon
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device class."""
|
||||
return self._device_class
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit the value is expressed in."""
|
||||
return self._unit
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the attribution."""
|
||||
return {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the station."""
|
||||
return f"{self._station} {self._label}"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique_id for this entity."""
|
||||
return f"{base_unique_id(self._latitude, self._longitude)}_{self._type}"
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return if state is available."""
|
||||
if self.coordinator.last_update_success_time:
|
||||
last_success_time = (
|
||||
utcnow() - self.coordinator.last_update_success_time
|
||||
< OBSERVATION_VALID_TIME
|
||||
)
|
||||
else:
|
||||
last_success_time = False
|
||||
return self.coordinator.last_update_success or last_success_time
|
||||
|
||||
@property
|
||||
def entity_registry_enabled_default(self) -> bool:
|
||||
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||
return False
|
@ -1,6 +1,4 @@
|
||||
"""Support for NWS weather service."""
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_SUNNY,
|
||||
@ -42,15 +40,14 @@ from .const import (
|
||||
COORDINATOR_OBSERVATION,
|
||||
DAYNIGHT,
|
||||
DOMAIN,
|
||||
FORECAST_VALID_TIME,
|
||||
HOURLY,
|
||||
NWS_DATA,
|
||||
OBSERVATION_VALID_TIME,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
OBSERVATION_VALID_TIME = timedelta(minutes=20)
|
||||
FORECAST_VALID_TIME = timedelta(minutes=45)
|
||||
|
||||
|
||||
def convert_condition(time, weather):
|
||||
"""
|
||||
|
@ -32,3 +32,21 @@ def mock_simple_nws_config():
|
||||
instance.station = "ABC"
|
||||
instance.stations = ["ABC"]
|
||||
yield mock_nws
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def no_sensor():
|
||||
"""Remove sensors."""
|
||||
with patch(
|
||||
"homeassistant.components.nws.sensor.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def no_weather():
|
||||
"""Remove weather."""
|
||||
with patch(
|
||||
"homeassistant.components.nws.weather.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
@ -44,6 +44,7 @@ DEFAULT_STATIONS = ["ABC", "XYZ"]
|
||||
DEFAULT_OBSERVATION = {
|
||||
"temperature": 10,
|
||||
"seaLevelPressure": 100000,
|
||||
"barometricPressure": 100000,
|
||||
"relativeHumidity": 10,
|
||||
"windSpeed": 10,
|
||||
"windDirection": 180,
|
||||
@ -53,9 +54,45 @@ DEFAULT_OBSERVATION = {
|
||||
"timestamp": "2019-08-12T23:53:00+00:00",
|
||||
"iconTime": "day",
|
||||
"iconWeather": (("Fair/clear", None),),
|
||||
"dewpoint": 5,
|
||||
"windChill": 5,
|
||||
"heatIndex": 15,
|
||||
"windGust": 20,
|
||||
}
|
||||
|
||||
EXPECTED_OBSERVATION_IMPERIAL = {
|
||||
SENSOR_EXPECTED_OBSERVATION_METRIC = {
|
||||
"dewpoint": "5",
|
||||
"temperature": "10",
|
||||
"windChill": "5",
|
||||
"heatIndex": "15",
|
||||
"relativeHumidity": "10",
|
||||
"windSpeed": "10",
|
||||
"windGust": "20",
|
||||
"windDirection": "180",
|
||||
"barometricPressure": "100000",
|
||||
"seaLevelPressure": "100000",
|
||||
"visibility": "10000",
|
||||
}
|
||||
|
||||
SENSOR_EXPECTED_OBSERVATION_IMPERIAL = {
|
||||
"dewpoint": str(round(convert_temperature(5, TEMP_CELSIUS, TEMP_FAHRENHEIT))),
|
||||
"temperature": str(round(convert_temperature(10, TEMP_CELSIUS, TEMP_FAHRENHEIT))),
|
||||
"windChill": str(round(convert_temperature(5, TEMP_CELSIUS, TEMP_FAHRENHEIT))),
|
||||
"heatIndex": str(round(convert_temperature(15, TEMP_CELSIUS, TEMP_FAHRENHEIT))),
|
||||
"relativeHumidity": "10",
|
||||
"windSpeed": str(round(convert_distance(10, LENGTH_KILOMETERS, LENGTH_MILES))),
|
||||
"windGust": str(round(convert_distance(20, LENGTH_KILOMETERS, LENGTH_MILES))),
|
||||
"windDirection": "180",
|
||||
"barometricPressure": str(
|
||||
round(convert_pressure(100000, PRESSURE_PA, PRESSURE_INHG), 2)
|
||||
),
|
||||
"seaLevelPressure": str(
|
||||
round(convert_pressure(100000, PRESSURE_PA, PRESSURE_INHG), 2)
|
||||
),
|
||||
"visibility": str(round(convert_distance(10000, LENGTH_METERS, LENGTH_MILES))),
|
||||
}
|
||||
|
||||
WEATHER_EXPECTED_OBSERVATION_IMPERIAL = {
|
||||
ATTR_WEATHER_TEMPERATURE: round(
|
||||
convert_temperature(10, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
),
|
||||
@ -72,7 +109,7 @@ EXPECTED_OBSERVATION_IMPERIAL = {
|
||||
ATTR_WEATHER_HUMIDITY: 10,
|
||||
}
|
||||
|
||||
EXPECTED_OBSERVATION_METRIC = {
|
||||
WEATHER_EXPECTED_OBSERVATION_METRIC = {
|
||||
ATTR_WEATHER_TEMPERATURE: 10,
|
||||
ATTR_WEATHER_WIND_BEARING: 180,
|
||||
ATTR_WEATHER_WIND_SPEED: 10,
|
||||
|
95
tests/components/nws/test_sensor.py
Normal file
95
tests/components/nws/test_sensor.py
Normal file
@ -0,0 +1,95 @@
|
||||
"""Sensors for National Weather Service (NWS)."""
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.nws.const import (
|
||||
ATTR_LABEL,
|
||||
ATTRIBUTION,
|
||||
DOMAIN,
|
||||
SENSOR_TYPES,
|
||||
)
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, STATE_UNKNOWN
|
||||
from homeassistant.util import slugify
|
||||
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.nws.const import (
|
||||
EXPECTED_FORECAST_IMPERIAL,
|
||||
EXPECTED_FORECAST_METRIC,
|
||||
NONE_OBSERVATION,
|
||||
NWS_CONFIG,
|
||||
SENSOR_EXPECTED_OBSERVATION_IMPERIAL,
|
||||
SENSOR_EXPECTED_OBSERVATION_METRIC,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"units,result_observation,result_forecast",
|
||||
[
|
||||
(
|
||||
IMPERIAL_SYSTEM,
|
||||
SENSOR_EXPECTED_OBSERVATION_IMPERIAL,
|
||||
EXPECTED_FORECAST_IMPERIAL,
|
||||
),
|
||||
(METRIC_SYSTEM, SENSOR_EXPECTED_OBSERVATION_METRIC, EXPECTED_FORECAST_METRIC),
|
||||
],
|
||||
)
|
||||
async def test_imperial_metric(
|
||||
hass, units, result_observation, result_forecast, mock_simple_nws, no_weather
|
||||
):
|
||||
"""Test with imperial and metric units."""
|
||||
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
for sensor_name, sensor_data in SENSOR_TYPES.items():
|
||||
registry.async_get_or_create(
|
||||
SENSOR_DOMAIN,
|
||||
DOMAIN,
|
||||
f"35_-75_{sensor_name}",
|
||||
suggested_object_id=f"abc_{sensor_data[ATTR_LABEL]}",
|
||||
disabled_by=None,
|
||||
)
|
||||
|
||||
hass.config.units = units
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=NWS_CONFIG,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for sensor_name, sensor_data in SENSOR_TYPES.items():
|
||||
state = hass.states.get(f"sensor.abc_{slugify(sensor_data[ATTR_LABEL])}")
|
||||
assert state
|
||||
assert state.state == result_observation[sensor_name]
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
|
||||
|
||||
async def test_none_values(hass, mock_simple_nws, no_weather):
|
||||
"""Test with no values."""
|
||||
instance = mock_simple_nws.return_value
|
||||
instance.observation = NONE_OBSERVATION
|
||||
|
||||
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
for sensor_name, sensor_data in SENSOR_TYPES.items():
|
||||
registry.async_get_or_create(
|
||||
SENSOR_DOMAIN,
|
||||
DOMAIN,
|
||||
f"35_-75_{sensor_name}",
|
||||
suggested_object_id=f"abc_{sensor_data[ATTR_LABEL]}",
|
||||
disabled_by=None,
|
||||
)
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=NWS_CONFIG,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for sensor_name, sensor_data in SENSOR_TYPES.items():
|
||||
state = hass.states.get(f"sensor.abc_{slugify(sensor_data[ATTR_LABEL])}")
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
@ -21,23 +21,27 @@ from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.components.nws.const import (
|
||||
EXPECTED_FORECAST_IMPERIAL,
|
||||
EXPECTED_FORECAST_METRIC,
|
||||
EXPECTED_OBSERVATION_IMPERIAL,
|
||||
EXPECTED_OBSERVATION_METRIC,
|
||||
NONE_FORECAST,
|
||||
NONE_OBSERVATION,
|
||||
NWS_CONFIG,
|
||||
WEATHER_EXPECTED_OBSERVATION_IMPERIAL,
|
||||
WEATHER_EXPECTED_OBSERVATION_METRIC,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"units,result_observation,result_forecast",
|
||||
[
|
||||
(IMPERIAL_SYSTEM, EXPECTED_OBSERVATION_IMPERIAL, EXPECTED_FORECAST_IMPERIAL),
|
||||
(METRIC_SYSTEM, EXPECTED_OBSERVATION_METRIC, EXPECTED_FORECAST_METRIC),
|
||||
(
|
||||
IMPERIAL_SYSTEM,
|
||||
WEATHER_EXPECTED_OBSERVATION_IMPERIAL,
|
||||
EXPECTED_FORECAST_IMPERIAL,
|
||||
),
|
||||
(METRIC_SYSTEM, WEATHER_EXPECTED_OBSERVATION_METRIC, EXPECTED_FORECAST_METRIC),
|
||||
],
|
||||
)
|
||||
async def test_imperial_metric(
|
||||
hass, units, result_observation, result_forecast, mock_simple_nws
|
||||
hass, units, result_observation, result_forecast, mock_simple_nws, no_sensor
|
||||
):
|
||||
"""Test with imperial and metric units."""
|
||||
# enable the hourly entity
|
||||
@ -86,7 +90,7 @@ async def test_imperial_metric(
|
||||
assert forecast[0].get(key) == value
|
||||
|
||||
|
||||
async def test_none_values(hass, mock_simple_nws):
|
||||
async def test_none_values(hass, mock_simple_nws, no_sensor):
|
||||
"""Test with none values in observation and forecast dicts."""
|
||||
instance = mock_simple_nws.return_value
|
||||
instance.observation = NONE_OBSERVATION
|
||||
@ -103,7 +107,7 @@ async def test_none_values(hass, mock_simple_nws):
|
||||
state = hass.states.get("weather.abc_daynight")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
data = state.attributes
|
||||
for key in EXPECTED_OBSERVATION_IMPERIAL:
|
||||
for key in WEATHER_EXPECTED_OBSERVATION_IMPERIAL:
|
||||
assert data.get(key) is None
|
||||
|
||||
forecast = data.get(ATTR_FORECAST)
|
||||
@ -111,7 +115,7 @@ async def test_none_values(hass, mock_simple_nws):
|
||||
assert forecast[0].get(key) is None
|
||||
|
||||
|
||||
async def test_none(hass, mock_simple_nws):
|
||||
async def test_none(hass, mock_simple_nws, no_sensor):
|
||||
"""Test with None as observation and forecast."""
|
||||
instance = mock_simple_nws.return_value
|
||||
instance.observation = None
|
||||
@ -130,14 +134,14 @@ async def test_none(hass, mock_simple_nws):
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
data = state.attributes
|
||||
for key in EXPECTED_OBSERVATION_IMPERIAL:
|
||||
for key in WEATHER_EXPECTED_OBSERVATION_IMPERIAL:
|
||||
assert data.get(key) is None
|
||||
|
||||
forecast = data.get(ATTR_FORECAST)
|
||||
assert forecast is None
|
||||
|
||||
|
||||
async def test_error_station(hass, mock_simple_nws):
|
||||
async def test_error_station(hass, mock_simple_nws, no_sensor):
|
||||
"""Test error in setting station."""
|
||||
|
||||
instance = mock_simple_nws.return_value
|
||||
@ -155,7 +159,7 @@ async def test_error_station(hass, mock_simple_nws):
|
||||
assert hass.states.get("weather.abc_daynight") is None
|
||||
|
||||
|
||||
async def test_entity_refresh(hass, mock_simple_nws):
|
||||
async def test_entity_refresh(hass, mock_simple_nws, no_sensor):
|
||||
"""Test manual refresh."""
|
||||
instance = mock_simple_nws.return_value
|
||||
|
||||
@ -184,7 +188,7 @@ async def test_entity_refresh(hass, mock_simple_nws):
|
||||
instance.update_forecast_hourly.assert_called_once()
|
||||
|
||||
|
||||
async def test_error_observation(hass, mock_simple_nws):
|
||||
async def test_error_observation(hass, mock_simple_nws, no_sensor):
|
||||
"""Test error during update observation."""
|
||||
utc_time = dt_util.utcnow()
|
||||
with patch("homeassistant.components.nws.utcnow") as mock_utc, patch(
|
||||
@ -248,7 +252,7 @@ async def test_error_observation(hass, mock_simple_nws):
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_error_forecast(hass, mock_simple_nws):
|
||||
async def test_error_forecast(hass, mock_simple_nws, no_sensor):
|
||||
"""Test error during update forecast."""
|
||||
instance = mock_simple_nws.return_value
|
||||
instance.update_forecast.side_effect = aiohttp.ClientError
|
||||
@ -279,7 +283,7 @@ async def test_error_forecast(hass, mock_simple_nws):
|
||||
assert state.state == ATTR_CONDITION_SUNNY
|
||||
|
||||
|
||||
async def test_error_forecast_hourly(hass, mock_simple_nws):
|
||||
async def test_error_forecast_hourly(hass, mock_simple_nws, no_sensor):
|
||||
"""Test error during update forecast hourly."""
|
||||
instance = mock_simple_nws.return_value
|
||||
instance.update_forecast_hourly.side_effect = aiohttp.ClientError
|
||||
@ -320,7 +324,7 @@ async def test_error_forecast_hourly(hass, mock_simple_nws):
|
||||
assert state.state == ATTR_CONDITION_SUNNY
|
||||
|
||||
|
||||
async def test_forecast_hourly_disable_enable(hass, mock_simple_nws):
|
||||
async def test_forecast_hourly_disable_enable(hass, mock_simple_nws, no_sensor):
|
||||
"""Test error during update forecast hourly."""
|
||||
entry = MockConfigEntry(
|
||||
domain=nws.DOMAIN,
|
||||
|
Loading…
Reference in New Issue
Block a user