Skip to content

Commit

Permalink
Adjust OpenUV integration for upcoming API limit changes (home-assist…
Browse files Browse the repository at this point in the history
…ant#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
  • Loading branch information
bachya authored and fabaff committed Jan 14, 2019
1 parent fff3cb0 commit ef79566
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 71 deletions.
21 changes: 4 additions & 17 deletions homeassistant/components/openuv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,21 @@
https://home-assistant.io/components/openuv/
"""
import logging
from datetime import timedelta

import voluptuous as vol

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__)
Expand Down Expand Up @@ -93,8 +91,6 @@
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)

Expand All @@ -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:
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand Down
19 changes: 8 additions & 11 deletions homeassistant/components/openuv/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
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
Expand Down Expand Up @@ -54,7 +53,8 @@ async def async_step_import(self, import_config):

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()
Expand All @@ -66,14 +66,11 @@ async def async_step_user(self, user_input=None):
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)
4 changes: 0 additions & 4 deletions homeassistant/components/openuv/const.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
"""Define constants for the OpenUV component."""
from datetime import timedelta

DOMAIN = 'openuv'

DEFAULT_SCAN_INTERVAL = timedelta(minutes=30)
5 changes: 5 additions & 0 deletions homeassistant/components/openuv/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Describes the format for available OpenUV services

---
update_data:
description: Request new data from OpenUV.
82 changes: 43 additions & 39 deletions tests/components/openuv/test_config_flow.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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',
Expand All @@ -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):
Expand All @@ -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',
Expand All @@ -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,
}

0 comments on commit ef79566

Please sign in to comment.