Adjust OpenUV integration for upcoming API limit changes (#19949)

* Adjust OpenUV integration for upcoming API limit changes

* Added fix for "Invalid API Key"

* Bugfix

* Add initial nighttime check

* Move from polling to a service-based model

* Fixed test

* Removed unnecessary scan interval

* Fixed test

* Moving test imports

* Member comments

* Hound

* Removed unused import
This commit is contained in:
Aaron Bach 2019-01-14 05:12:06 -07:00 committed by Fabian Affolter
parent fff3cb0b46
commit ef79566864
5 changed files with 60 additions and 71 deletions

View file

@ -5,7 +5,6 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/openuv/
"""
import logging
from datetime import timedelta
import voluptuous as vol
@ -13,15 +12,14 @@ from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import (
ATTR_ATTRIBUTION, CONF_API_KEY, CONF_BINARY_SENSORS, CONF_ELEVATION,
CONF_LATITUDE, CONF_LONGITUDE, CONF_MONITORED_CONDITIONS,
CONF_SCAN_INTERVAL, CONF_SENSORS)
CONF_SENSORS)
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
from .config_flow import configured_instances
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN
from .const import DOMAIN
REQUIREMENTS = ['pyopenuv==1.0.4']
_LOGGER = logging.getLogger(__name__)
@ -93,8 +91,6 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_BINARY_SENSORS, default={}):
BINARY_SENSOR_SCHEMA,
vol.Optional(CONF_SENSORS, default={}): SENSOR_SCHEMA,
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL):
cv.time_period,
})
}, extra=vol.ALLOW_EXTRA)
@ -120,7 +116,6 @@ async def async_setup(hass, config):
CONF_API_KEY: conf[CONF_API_KEY],
CONF_BINARY_SENSORS: conf[CONF_BINARY_SENSORS],
CONF_SENSORS: conf[CONF_SENSORS],
CONF_SCAN_INTERVAL: conf[CONF_SCAN_INTERVAL],
}
if CONF_LATITUDE in conf:
@ -167,17 +162,13 @@ async def async_setup_entry(hass, config_entry):
hass.config_entries.async_forward_entry_setup(
config_entry, component))
async def refresh(event_time):
async def update_data(service):
"""Refresh OpenUV data."""
_LOGGER.debug('Refreshing OpenUV data')
await openuv.async_update()
async_dispatcher_send(hass, TOPIC_UPDATE)
hass.data[DOMAIN][DATA_OPENUV_LISTENER][
config_entry.entry_id] = async_track_time_interval(
hass,
refresh,
timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL]))
hass.services.async_register(DOMAIN, 'update_data', update_data)
return True
@ -186,10 +177,6 @@ async def async_unload_entry(hass, config_entry):
"""Unload an OpenUV config entry."""
hass.data[DOMAIN][DATA_OPENUV_CLIENT].pop(config_entry.entry_id)
remove_listener = hass.data[DOMAIN][DATA_OPENUV_LISTENER].pop(
config_entry.entry_id)
remove_listener()
for component in ('binary_sensor', 'sensor'):
await hass.config_entries.async_forward_entry_unload(
config_entry, component)

View file

@ -5,11 +5,10 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.core import callback
from homeassistant.const import (
CONF_API_KEY, CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE,
CONF_SCAN_INTERVAL)
CONF_API_KEY, CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE)
from homeassistant.helpers import aiohttp_client, config_validation as cv
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN
from .const import DOMAIN
@callback
@ -54,7 +53,8 @@ class OpenUvFlowHandler(config_entries.ConfigFlow):
async def async_step_user(self, user_input=None):
"""Handle the start of the config flow."""
from pyopenuv.util import validate_api_key
from pyopenuv import Client
from pyopenuv.errors import OpenUvError
if not user_input:
return await self._show_form()
@ -66,14 +66,11 @@ class OpenUvFlowHandler(config_entries.ConfigFlow):
return await self._show_form({CONF_LATITUDE: 'identifier_exists'})
websession = aiohttp_client.async_get_clientsession(self.hass)
api_key_validation = await validate_api_key(
user_input[CONF_API_KEY], websession)
client = Client(user_input[CONF_API_KEY], 0, 0, websession)
if not api_key_validation:
try:
await client.uv_index()
except OpenUvError:
return await self._show_form({CONF_API_KEY: 'invalid_api_key'})
scan_interval = user_input.get(
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
user_input[CONF_SCAN_INTERVAL] = scan_interval.seconds
return self.async_create_entry(title=identifier, data=user_input)

View file

@ -1,6 +1,2 @@
"""Define constants for the OpenUV component."""
from datetime import timedelta
DOMAIN = 'openuv'
DEFAULT_SCAN_INTERVAL = timedelta(minutes=30)

View file

@ -0,0 +1,5 @@
# Describes the format for available OpenUV services
---
update_data:
description: Request new data from OpenUV.

View file

@ -1,14 +1,27 @@
"""Define tests for the OpenUV config flow."""
from datetime import timedelta
from unittest.mock import patch
import pytest
from pyopenuv.errors import OpenUvError
from homeassistant import data_entry_flow
from homeassistant.components.openuv import DOMAIN, config_flow
from homeassistant.const import (
CONF_API_KEY, CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE,
CONF_SCAN_INTERVAL)
CONF_API_KEY, CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE)
from tests.common import MockConfigEntry, mock_coro
from tests.common import MockConfigEntry, MockDependency, mock_coro
@pytest.fixture
def uv_index_response():
"""Define a fixture for a successful /uv response."""
return mock_coro()
@pytest.fixture
def mock_pyopenuv(uv_index_response):
"""Mock the pyopenuv library."""
with MockDependency('pyopenuv') as mock_pyopenuv_:
mock_pyopenuv_.Client().uv_index.return_value = uv_index_response
yield mock_pyopenuv_
async def test_duplicate_error(hass):
@ -28,7 +41,9 @@ async def test_duplicate_error(hass):
assert result['errors'] == {CONF_LATITUDE: 'identifier_exists'}
async def test_invalid_api_key(hass):
@pytest.mark.parametrize(
'uv_index_response', [mock_coro(exception=OpenUvError)])
async def test_invalid_api_key(hass, mock_pyopenuv):
"""Test that an invalid API key throws an error."""
conf = {
CONF_API_KEY: '12345abcde',
@ -40,10 +55,8 @@ async def test_invalid_api_key(hass):
flow = config_flow.OpenUvFlowHandler()
flow.hass = hass
with patch('pyopenuv.util.validate_api_key',
return_value=mock_coro(False)):
result = await flow.async_step_user(user_input=conf)
assert result['errors'] == {CONF_API_KEY: 'invalid_api_key'}
result = await flow.async_step_user(user_input=conf)
assert result['errors'] == {CONF_API_KEY: 'invalid_api_key'}
async def test_show_form(hass):
@ -57,7 +70,7 @@ async def test_show_form(hass):
assert result['step_id'] == 'user'
async def test_step_import(hass):
async def test_step_import(hass, mock_pyopenuv):
"""Test that the import step works."""
conf = {
CONF_API_KEY: '12345abcde',
@ -69,44 +82,35 @@ async def test_step_import(hass):
flow = config_flow.OpenUvFlowHandler()
flow.hass = hass
with patch('pyopenuv.util.validate_api_key',
return_value=mock_coro(True)):
result = await flow.async_step_import(import_config=conf)
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result['title'] == '39.128712, -104.9812612'
assert result['data'] == {
CONF_API_KEY: '12345abcde',
CONF_ELEVATION: 59.1234,
CONF_LATITUDE: 39.128712,
CONF_LONGITUDE: -104.9812612,
CONF_SCAN_INTERVAL: 1800,
}
result = await flow.async_step_import(import_config=conf)
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result['title'] == '39.128712, -104.9812612'
assert result['data'] == {
CONF_API_KEY: '12345abcde',
CONF_ELEVATION: 59.1234,
CONF_LATITUDE: 39.128712,
CONF_LONGITUDE: -104.9812612,
}
async def test_step_user(hass):
async def test_step_user(hass, mock_pyopenuv):
"""Test that the user step works."""
conf = {
CONF_API_KEY: '12345abcde',
CONF_ELEVATION: 59.1234,
CONF_LATITUDE: 39.128712,
CONF_LONGITUDE: -104.9812612,
CONF_SCAN_INTERVAL: timedelta(minutes=5)
}
flow = config_flow.OpenUvFlowHandler()
flow.hass = hass
with patch('pyopenuv.util.validate_api_key',
return_value=mock_coro(True)):
result = await flow.async_step_user(user_input=conf)
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result['title'] == '39.128712, -104.9812612'
assert result['data'] == {
CONF_API_KEY: '12345abcde',
CONF_ELEVATION: 59.1234,
CONF_LATITUDE: 39.128712,
CONF_LONGITUDE: -104.9812612,
CONF_SCAN_INTERVAL: 300,
}
result = await flow.async_step_user(user_input=conf)
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result['title'] == '39.128712, -104.9812612'
assert result['data'] == {
CONF_API_KEY: '12345abcde',
CONF_ELEVATION: 59.1234,
CONF_LATITUDE: 39.128712,
CONF_LONGITUDE: -104.9812612,
}