Skip to content

Commit

Permalink
Make WUnderground async (#12385)
Browse files Browse the repository at this point in the history
* 🐎 Async WUnderground

* ∞ Them lines be too long

* Fix pylint warnings

* Changes according to comments

* Remove STATE_UNKNOWN

* πŸ”¬ Fix tests

* Improve tests
  • Loading branch information
OttoWinter authored and balloob committed Feb 16, 2018
1 parent b3a4772 commit fe5626b
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 271 deletions.
51 changes: 28 additions & 23 deletions homeassistant/components/sensor/wunderground.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.wunderground/
"""
import asyncio
from datetime import timedelta
import logging

import re
import requests

import aiohttp
import async_timeout
import voluptuous as vol

from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.helpers.typing import HomeAssistantType, ConfigType
from homeassistant.components.sensor import PLATFORM_SCHEMA, ENTITY_ID_FORMAT
from homeassistant.const import (
CONF_MONITORED_CONDITIONS, CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE,
TEMP_FAHRENHEIT, TEMP_CELSIUS, LENGTH_INCHES, LENGTH_KILOMETERS,
LENGTH_MILES, LENGTH_FEET, STATE_UNKNOWN, ATTR_ATTRIBUTION)
LENGTH_MILES, LENGTH_FEET, ATTR_ATTRIBUTION)
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.entity import Entity, generate_entity_id
from homeassistant.helpers.entity import Entity, async_generate_entity_id
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv

Expand Down Expand Up @@ -627,7 +630,9 @@ def _get_attributes(rest):
})


def setup_platform(hass, config, add_devices, discovery_info=None):
@asyncio.coroutine
def async_setup_platform(hass: HomeAssistantType, config: ConfigType,
async_add_devices, discovery_info=None):
"""Set up the WUnderground sensor."""
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
Expand All @@ -639,13 +644,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for variable in config[CONF_MONITORED_CONDITIONS]:
sensors.append(WUndergroundSensor(hass, rest, variable))

rest.update()
yield from rest.async_update()
if not rest.data:
raise PlatformNotReady

add_devices(sensors)

return True
async_add_devices(sensors, True)


class WUndergroundSensor(Entity):
Expand All @@ -663,7 +666,7 @@ def __init__(self, hass: HomeAssistantType, rest, condition):
self._entity_picture = None
self._unit_of_measurement = self._cfg_expand("unit_of_measurement")
self.rest.request_feature(SENSOR_TYPES[condition].feature)
self.entity_id = generate_entity_id(
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, "pws_" + condition, hass=hass)

def _cfg_expand(self, what, default=None):
Expand Down Expand Up @@ -727,15 +730,16 @@ def unit_of_measurement(self):
"""Return the units of measurement."""
return self._unit_of_measurement

def update(self):
@asyncio.coroutine
def async_update(self):
"""Update current conditions."""
self.rest.update()
yield from self.rest.async_update()

if not self.rest.data:
# no data, return
return

self._state = self._cfg_expand("value", STATE_UNKNOWN)
self._state = self._cfg_expand("value")
self._update_attrs()
self._icon = self._cfg_expand("icon", super().icon)
url = self._cfg_expand("entity_picture")
Expand All @@ -757,35 +761,36 @@ def __init__(self, hass, api_key, pws_id, lang, latitude, longitude):
self._longitude = longitude
self._features = set()
self.data = None
self._session = async_get_clientsession(self._hass)

def request_feature(self, feature):
"""Register feature to be fetched from WU API."""
self._features.add(feature)

def _build_url(self, baseurl=_RESOURCE):
url = baseurl.format(
self._api_key, "/".join(self._features), self._lang)
self._api_key, '/'.join(sorted(self._features)), self._lang)
if self._pws_id:
url = url + 'pws:{}'.format(self._pws_id)
else:
url = url + '{},{}'.format(self._latitude, self._longitude)

return url + '.json'

@asyncio.coroutine
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
def async_update(self):
"""Get the latest data from WUnderground."""
try:
result = requests.get(self._build_url(), timeout=10).json()
with async_timeout.timeout(10, loop=self._hass.loop):
response = yield from self._session.get(self._build_url())
result = yield from response.json()
if "error" in result['response']:
raise ValueError(result['response']["error"]
["description"])
else:
self.data = result
return True
raise ValueError(result['response']["error"]["description"])
self.data = result
except ValueError as err:
_LOGGER.error("Check WUnderground API %s", err.args)
self.data = None
except requests.RequestException as err:
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
_LOGGER.error("Error fetching WUnderground data: %s", repr(err))
self.data = None
Loading

0 comments on commit fe5626b

Please sign in to comment.