Remove configuration.yaml support for OpenUV (#36148)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
Aaron Bach 2020-05-27 16:48:28 -06:00 committed by GitHub
parent 7571fdc957
commit c18ba6aec0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 173 deletions

View file

@ -4,9 +4,7 @@ import logging
from pyopenuv import Client from pyopenuv import Client
from pyopenuv.errors import OpenUvError from pyopenuv.errors import OpenUvError
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import ( from homeassistant.const import (
ATTR_ATTRIBUTION, ATTR_ATTRIBUTION,
CONF_API_KEY, CONF_API_KEY,
@ -26,7 +24,6 @@ from homeassistant.helpers.dispatcher import (
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.service import verify_domain_control from homeassistant.helpers.service import verify_domain_control
from .config_flow import configured_instances
from .const import DOMAIN from .const import DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -55,54 +52,14 @@ TYPE_SAFE_EXPOSURE_TIME_4 = "safe_exposure_time_type_4"
TYPE_SAFE_EXPOSURE_TIME_5 = "safe_exposure_time_type_5" TYPE_SAFE_EXPOSURE_TIME_5 = "safe_exposure_time_type_5"
TYPE_SAFE_EXPOSURE_TIME_6 = "safe_exposure_time_type_6" TYPE_SAFE_EXPOSURE_TIME_6 = "safe_exposure_time_type_6"
PLATFORMS = ["binary_sensor", "sensor"]
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = cv.deprecated(DOMAIN, invalidation_version="0.115")
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_API_KEY): cv.string,
vol.Optional(CONF_ELEVATION): float,
vol.Optional(CONF_LATITUDE): cv.latitude,
vol.Optional(CONF_LONGITUDE): cv.longitude,
}
)
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass, config): async def async_setup(hass, config):
"""Set up the OpenUV component.""" """Set up the OpenUV component."""
hass.data[DOMAIN] = {} hass.data[DOMAIN] = {DATA_OPENUV_CLIENT: {}, DATA_OPENUV_LISTENER: {}}
hass.data[DOMAIN][DATA_OPENUV_CLIENT] = {}
hass.data[DOMAIN][DATA_OPENUV_LISTENER] = {}
if DOMAIN not in config:
return True
conf = config[DOMAIN]
identifier = (
f"{conf.get(CONF_LATITUDE, hass.config.latitude)}, "
f"{conf.get(CONF_LONGITUDE, hass.config.longitude)}"
)
if identifier in configured_instances(hass):
return True
data = {CONF_API_KEY: conf[CONF_API_KEY]}
if CONF_LATITUDE in conf:
data[CONF_LATITUDE] = conf[CONF_LATITUDE]
if CONF_LONGITUDE in conf:
data[CONF_LONGITUDE] = conf[CONF_LONGITUDE]
if CONF_ELEVATION in conf:
data[CONF_ELEVATION] = conf[CONF_ELEVATION]
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=data
)
)
return True return True
@ -127,7 +84,7 @@ async def async_setup_entry(hass, config_entry):
_LOGGER.error("Config entry failed: %s", err) _LOGGER.error("Config entry failed: %s", err)
raise ConfigEntryNotReady raise ConfigEntryNotReady
for component in ("binary_sensor", "sensor"): for component in PLATFORMS:
hass.async_create_task( hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, component) hass.config_entries.async_forward_entry_setup(config_entry, component)
) )
@ -139,8 +96,6 @@ async def async_setup_entry(hass, config_entry):
await openuv.async_update() await openuv.async_update()
async_dispatcher_send(hass, TOPIC_UPDATE) async_dispatcher_send(hass, TOPIC_UPDATE)
hass.services.async_register(DOMAIN, "update_data", update_data)
@_verify_domain_control @_verify_domain_control
async def update_uv_index_data(service): async def update_uv_index_data(service):
"""Refresh OpenUV UV index data.""" """Refresh OpenUV UV index data."""
@ -148,8 +103,6 @@ async def async_setup_entry(hass, config_entry):
await openuv.async_update_uv_index_data() await openuv.async_update_uv_index_data()
async_dispatcher_send(hass, TOPIC_UPDATE) async_dispatcher_send(hass, TOPIC_UPDATE)
hass.services.async_register(DOMAIN, "update_uv_index_data", update_uv_index_data)
@_verify_domain_control @_verify_domain_control
async def update_protection_data(service): async def update_protection_data(service):
"""Refresh OpenUV protection window data.""" """Refresh OpenUV protection window data."""
@ -157,25 +110,30 @@ async def async_setup_entry(hass, config_entry):
await openuv.async_update_protection_data() await openuv.async_update_protection_data()
async_dispatcher_send(hass, TOPIC_UPDATE) async_dispatcher_send(hass, TOPIC_UPDATE)
hass.services.async_register( for service, method in [
DOMAIN, "update_protection_data", update_protection_data ("update_data", update_data),
) ("update_uv_index_data", update_uv_index_data),
("update_protection_data", update_protection_data),
]:
hass.services.async_register(DOMAIN, service, method)
return True return True
async def async_unload_entry(hass, config_entry): async def async_unload_entry(hass, config_entry):
"""Unload an OpenUV config entry.""" """Unload an OpenUV config entry."""
hass.data[DOMAIN][DATA_OPENUV_CLIENT].pop(config_entry.entry_id) unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(config_entry, component)
for component in PLATFORMS
]
)
)
if unload_ok:
hass.data[DOMAIN][DATA_OPENUV_CLIENT].pop(config_entry.entry_id)
tasks = [ return unload_ok
hass.config_entries.async_forward_entry_unload(config_entry, component)
for component in ("binary_sensor", "sensor")
]
await asyncio.gather(*tasks)
return True
async def async_migrate_entry(hass, config_entry): async def async_migrate_entry(hass, config_entry):

View file

@ -10,24 +10,21 @@ from homeassistant.const import (
CONF_LATITUDE, CONF_LATITUDE,
CONF_LONGITUDE, CONF_LONGITUDE,
) )
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers import aiohttp_client, config_validation as cv
from .const import DOMAIN from .const import DOMAIN # pylint: disable=unused-import
CONFIG_SCHEMA = vol.Schema(
@callback {
def configured_instances(hass): vol.Required(CONF_API_KEY): str,
"""Return a set of configured OpenUV instances.""" vol.Inclusive(CONF_LATITUDE, "coords"): cv.latitude,
return { vol.Inclusive(CONF_LONGITUDE, "coords"): cv.longitude,
f"{entry.data.get(CONF_LATITUDE, hass.config.latitude)}, " vol.Optional(CONF_ELEVATION): vol.Coerce(float),
f"{entry.data.get(CONF_LONGITUDE, hass.config.longitude)}"
for entry in hass.config_entries.async_entries(DOMAIN)
} }
)
@config_entries.HANDLERS.register(DOMAIN) class OpenUvFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
class OpenUvFlowHandler(config_entries.ConfigFlow):
"""Handle an OpenUV config flow.""" """Handle an OpenUV config flow."""
VERSION = 2 VERSION = 2
@ -35,17 +32,8 @@ class OpenUvFlowHandler(config_entries.ConfigFlow):
async def _show_form(self, errors=None): async def _show_form(self, errors=None):
"""Show the form to the user.""" """Show the form to the user."""
data_schema = vol.Schema(
{
vol.Required(CONF_API_KEY): str,
vol.Optional(CONF_LATITUDE): cv.latitude,
vol.Optional(CONF_LONGITUDE): cv.longitude,
vol.Optional(CONF_ELEVATION): vol.Coerce(float),
}
)
return self.async_show_form( return self.async_show_form(
step_id="user", data_schema=data_schema, errors=errors if errors else {} step_id="user", data_schema=CONFIG_SCHEMA, errors=errors if errors else {},
) )
async def async_step_import(self, import_config): async def async_step_import(self, import_config):
@ -54,16 +42,16 @@ class OpenUvFlowHandler(config_entries.ConfigFlow):
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Handle the start of the config flow.""" """Handle the start of the config flow."""
if not user_input: if not user_input:
return await self._show_form() return await self._show_form()
identifier = ( if user_input.get(CONF_LATITUDE):
f"{user_input.get(CONF_LATITUDE, self.hass.config.latitude)}, " identifier = f"{user_input[CONF_LATITUDE]}, {user_input[CONF_LONGITUDE]}"
f"{user_input.get(CONF_LONGITUDE, self.hass.config.longitude)}" else:
) identifier = "Default Coordinates"
if identifier in configured_instances(self.hass):
return await self._show_form({CONF_LATITUDE: "identifier_exists"}) await self.async_set_unique_id(identifier)
self._abort_if_unique_id_configured()
websession = aiohttp_client.async_get_clientsession(self.hass) websession = aiohttp_client.async_get_clientsession(self.hass)
client = Client(user_input[CONF_API_KEY], 0, 0, websession) client = Client(user_input[CONF_API_KEY], 0, 0, websession)

View file

@ -13,7 +13,10 @@
}, },
"error": { "error": {
"identifier_exists": "Coordinates already registered", "identifier_exists": "Coordinates already registered",
"invalid_api_key": "Invalid API key" "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]"
},
"abort": {
"already_configured": "These coordinates are already registered."
} }
} }
} }

View file

@ -1,5 +1,8 @@
{ {
"config": { "config": {
"abort": {
"already_configured": "These coordinates are already registered."
},
"error": { "error": {
"identifier_exists": "Coordinates already registered", "identifier_exists": "Coordinates already registered",
"invalid_api_key": "Invalid API key" "invalid_api_key": "Invalid API key"
@ -7,7 +10,7 @@
"step": { "step": {
"user": { "user": {
"data": { "data": {
"api_key": "API Key", "api_key": "[%key:common::config_flow::data::api_key%]",
"elevation": "Elevation", "elevation": "Elevation",
"latitude": "Latitude", "latitude": "Latitude",
"longitude": "Longitude" "longitude": "Longitude"

View file

@ -1,11 +1,9 @@
"""Define tests for the OpenUV config flow.""" """Define tests for the OpenUV config flow."""
from unittest.mock import patch from pyopenuv.errors import InvalidApiKeyError
from pyopenuv.errors import OpenUvError
import pytest
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.components.openuv import DOMAIN, config_flow from homeassistant.components.openuv import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import ( from homeassistant.const import (
CONF_API_KEY, CONF_API_KEY,
CONF_ELEVATION, CONF_ELEVATION,
@ -13,21 +11,8 @@ from homeassistant.const import (
CONF_LONGITUDE, CONF_LONGITUDE,
) )
from tests.common import MockConfigEntry, mock_coro from tests.async_mock import patch
from tests.common import MockConfigEntry
@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 patch("homeassistant.components.openuv.config_flow.Client") as MockClient:
MockClient().uv_index.return_value = uv_index_response
yield MockClient
async def test_duplicate_error(hass): async def test_duplicate_error(hass):
@ -39,16 +24,19 @@ async def test_duplicate_error(hass):
CONF_LONGITUDE: -104.9812612, CONF_LONGITUDE: -104.9812612,
} }
MockConfigEntry(domain=DOMAIN, data=conf).add_to_hass(hass) MockConfigEntry(
flow = config_flow.OpenUvFlowHandler() domain=DOMAIN, unique_id="39.128712, -104.9812612", data=conf
flow.hass = hass ).add_to_hass(hass)
result = await flow.async_step_user(user_input=conf) result = await hass.config_entries.flow.async_init(
assert result["errors"] == {CONF_LATITUDE: "identifier_exists"} DOMAIN, context={"source": SOURCE_USER}, data=conf
)
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
@pytest.mark.parametrize("uv_index_response", [mock_coro(exception=OpenUvError)]) async def test_invalid_api_key(hass):
async def test_invalid_api_key(hass, mock_pyopenuv):
"""Test that an invalid API key throws an error.""" """Test that an invalid API key throws an error."""
conf = { conf = {
CONF_API_KEY: "12345abcde", CONF_API_KEY: "12345abcde",
@ -57,48 +45,17 @@ async def test_invalid_api_key(hass, mock_pyopenuv):
CONF_LONGITUDE: -104.9812612, CONF_LONGITUDE: -104.9812612,
} }
flow = config_flow.OpenUvFlowHandler() with patch(
flow.hass = hass "pyopenuv.client.Client.uv_index", side_effect=InvalidApiKeyError,
):
result = await flow.async_step_user(user_input=conf) result = await hass.config_entries.flow.async_init(
assert result["errors"] == {CONF_API_KEY: "invalid_api_key"} DOMAIN, context={"source": SOURCE_USER}, data=conf
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["errors"] == {CONF_API_KEY: "invalid_api_key"}
async def test_show_form(hass): async def test_step_user(hass):
"""Test that the form is served with no input."""
flow = config_flow.OpenUvFlowHandler()
flow.hass = hass
result = await flow.async_step_user(user_input=None)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
async def test_step_import(hass, mock_pyopenuv):
"""Test that the import step works."""
conf = {
CONF_API_KEY: "12345abcde",
CONF_ELEVATION: 59.1234,
CONF_LATITUDE: 39.128712,
CONF_LONGITUDE: -104.9812612,
}
flow = config_flow.OpenUvFlowHandler()
flow.hass = hass
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, mock_pyopenuv):
"""Test that the user step works.""" """Test that the user step works."""
conf = { conf = {
CONF_API_KEY: "12345abcde", CONF_API_KEY: "12345abcde",
@ -107,15 +64,23 @@ async def test_step_user(hass, mock_pyopenuv):
CONF_LONGITUDE: -104.9812612, CONF_LONGITUDE: -104.9812612,
} }
flow = config_flow.OpenUvFlowHandler() with patch(
flow.hass = hass "homeassistant.components.airvisual.async_setup_entry", return_value=True
), patch("pyopenuv.client.Client.uv_index"):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
result = await flow.async_step_user(user_input=conf) result = await hass.config_entries.flow.async_init(
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY DOMAIN, context={"source": SOURCE_USER}, data=conf
assert result["title"] == "39.128712, -104.9812612" )
assert result["data"] == { assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
CONF_API_KEY: "12345abcde", assert result["title"] == "39.128712, -104.9812612"
CONF_ELEVATION: 59.1234, assert result["data"] == {
CONF_LATITUDE: 39.128712, CONF_API_KEY: "12345abcde",
CONF_LONGITUDE: -104.9812612, CONF_ELEVATION: 59.1234,
} CONF_LATITUDE: 39.128712,
CONF_LONGITUDE: -104.9812612,
}