Update pyipma to 2.0 (#30746)

* update ipma component for pyipma 2.0

* fix wind speed; refactor forecast

* update requirements*.txt

* fix tests; update CODEOWNERS; update pyipma to 2.0.1

* minor changes as suggested in PR

* make lint happy

* fix mocking coroutines

* restore old unique id

* fix station lat/lon; update pyipma version
This commit is contained in:
Abílio Costa 2020-01-21 16:04:22 +00:00 committed by springstan
parent c2df4f56a3
commit 2aff913d9b
6 changed files with 138 additions and 110 deletions

View file

@ -3,7 +3,8 @@ from datetime import timedelta
import logging
import async_timeout
from pyipma import Station
from pyipma.api import IPMA_API
from pyipma.location import Location
import voluptuous as vol
from homeassistant.components.weather import (
@ -24,8 +25,6 @@ _LOGGER = logging.getLogger(__name__)
ATTRIBUTION = "Instituto Português do Mar e Atmosfera"
ATTR_WEATHER_DESCRIPTION = "description"
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)
CONDITION_CLASSES = {
@ -68,9 +67,10 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
return
station = await async_get_station(hass, latitude, longitude)
api = await async_get_api(hass)
location = await async_get_location(hass, api, latitude, longitude)
async_add_entities([IPMAWeather(station, config)], True)
async_add_entities([IPMAWeather(location, api, config)], True)
async def async_setup_entry(hass, config_entry, async_add_entities):
@ -78,61 +78,71 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
latitude = config_entry.data[CONF_LATITUDE]
longitude = config_entry.data[CONF_LONGITUDE]
station = await async_get_station(hass, latitude, longitude)
api = await async_get_api(hass)
location = await async_get_location(hass, api, latitude, longitude)
async_add_entities([IPMAWeather(station, config_entry.data)], True)
async_add_entities([IPMAWeather(location, api, config_entry.data)], True)
async def async_get_station(hass, latitude, longitude):
"""Retrieve weather station, station name to be used as the entity name."""
async def async_get_api(hass):
"""Get the pyipma api object."""
websession = async_get_clientsession(hass)
return IPMA_API(websession)
async def async_get_location(hass, api, latitude, longitude):
"""Retrieve pyipma location, location name to be used as the entity name."""
with async_timeout.timeout(10):
station = await Station.get(websession, float(latitude), float(longitude))
location = await Location.get(api, float(latitude), float(longitude))
_LOGGER.debug(
"Initializing for coordinates %s, %s -> station %s",
latitude,
longitude,
station.local,
location.station,
)
return station
return location
class IPMAWeather(WeatherEntity):
"""Representation of a weather condition."""
def __init__(self, station, config):
def __init__(self, location: Location, api: IPMA_API, config):
"""Initialise the platform with a data instance and station name."""
self._station_name = config.get(CONF_NAME, station.local)
self._station = station
self._condition = None
self._api = api
self._location_name = config.get(CONF_NAME, location.name)
self._location = location
self._observation = None
self._forecast = None
self._description = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self):
"""Update Condition and Forecast."""
with async_timeout.timeout(10):
_new_condition = await self._station.observation()
if _new_condition is None:
_LOGGER.warning("Could not update weather conditions")
return
self._condition = _new_condition
new_observation = await self._location.observation(self._api)
new_forecast = await self._location.forecast(self._api)
if new_observation:
self._observation = new_observation
else:
_LOGGER.warning("Could not update weather observation")
if new_forecast:
self._forecast = [f for f in new_forecast if f.forecasted_hours == 24]
else:
_LOGGER.warning("Could not update weather forecast")
_LOGGER.debug(
"Updating station %s, condition %s",
self._station.local,
self._condition,
"Updated location %s, observation %s",
self._location.name,
self._observation,
)
self._forecast = await self._station.forecast()
self._description = self._forecast[0].description
@property
def unique_id(self) -> str:
"""Return a unique id."""
return f"{self._station.latitude}, {self._station.longitude}"
return f"{self._location.station_latitude}, {self._location.station_longitude}"
@property
def attribution(self):
@ -142,7 +152,7 @@ class IPMAWeather(WeatherEntity):
@property
def name(self):
"""Return the name of the station."""
return self._station_name
return self._location_name
@property
def condition(self):
@ -154,7 +164,7 @@ class IPMAWeather(WeatherEntity):
(
k
for k, v in CONDITION_CLASSES.items()
if self._forecast[0].idWeatherType in v
if self._forecast[0].weather_type in v
),
None,
)
@ -162,42 +172,42 @@ class IPMAWeather(WeatherEntity):
@property
def temperature(self):
"""Return the current temperature."""
if not self._condition:
if not self._observation:
return None
return self._condition.temperature
return self._observation.temperature
@property
def pressure(self):
"""Return the current pressure."""
if not self._condition:
if not self._observation:
return None
return self._condition.pressure
return self._observation.pressure
@property
def humidity(self):
"""Return the name of the sensor."""
if not self._condition:
if not self._observation:
return None
return self._condition.humidity
return self._observation.humidity
@property
def wind_speed(self):
"""Return the current windspeed."""
if not self._condition:
if not self._observation:
return None
return self._condition.windspeed
return self._observation.wind_intensity_km
@property
def wind_bearing(self):
"""Return the current wind bearing (degrees)."""
if not self._condition:
if not self._observation:
return None
return self._condition.winddirection
return self._observation.wind_direction
@property
def temperature_unit(self):
@ -207,33 +217,25 @@ class IPMAWeather(WeatherEntity):
@property
def forecast(self):
"""Return the forecast array."""
if self._forecast:
fcdata_out = []
for data_in in self._forecast:
data_out = {}
data_out[ATTR_FORECAST_TIME] = data_in.forecastDate
data_out[ATTR_FORECAST_CONDITION] = next(
if not self._forecast:
return []
fcdata_out = [
{
ATTR_FORECAST_TIME: data_in.forecast_date,
ATTR_FORECAST_CONDITION: next(
(
k
for k, v in CONDITION_CLASSES.items()
if int(data_in.idWeatherType) in v
if int(data_in.weather_type) in v
),
None,
)
data_out[ATTR_FORECAST_TEMP_LOW] = data_in.tMin
data_out[ATTR_FORECAST_TEMP] = data_in.tMax
data_out[ATTR_FORECAST_PRECIPITATION] = data_in.precipitaProb
),
ATTR_FORECAST_TEMP_LOW: data_in.min_temperature,
ATTR_FORECAST_TEMP: data_in.max_temperature,
ATTR_FORECAST_PRECIPITATION: data_in.precipitation_probability,
}
for data_in in self._forecast
]
fcdata_out.append(data_out)
return fcdata_out
@property
def device_state_attributes(self):
"""Return the state attributes."""
data = dict()
if self._description:
data[ATTR_WEATHER_DESCRIPTION] = self._description
return data
return fcdata_out