Add config flow for OpenUV (#16159)

* OpenUV config flow in place

* Test folder in place

* Owner-requested comments

* Tests

* More tests

* Owner-requested changes (part 1 of 2)

* Updated requirements

* Owner-requested changes (2 of 2)

* Removed unnecessary import

* Bumping Travis

* Updated requirements

* More requirements

* Updated tests

* Owner-requested changes

* Hound

* Updated docstring
This commit is contained in:
Aaron Bach 2018-09-04 01:22:44 -06:00 committed by Paulus Schoutsen
parent 7a6facc875
commit f96aee2832
14 changed files with 348 additions and 66 deletions

View file

@ -228,7 +228,7 @@ omit =
homeassistant/components/opencv.py homeassistant/components/opencv.py
homeassistant/components/*/opencv.py homeassistant/components/*/opencv.py
homeassistant/components/openuv.py homeassistant/components/openuv/__init__.py
homeassistant/components/*/openuv.py homeassistant/components/*/openuv.py
homeassistant/components/pilight.py homeassistant/components/pilight.py

View file

@ -7,12 +7,11 @@ https://home-assistant.io/components/binary_sensor.openuv/
import logging import logging
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_MONITORED_CONDITIONS
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.components.openuv import ( from homeassistant.components.openuv import (
BINARY_SENSORS, DATA_PROTECTION_WINDOW, DOMAIN, TOPIC_UPDATE, BINARY_SENSORS, DATA_OPENUV_CLIENT, DATA_PROTECTION_WINDOW, DOMAIN,
TYPE_PROTECTION_WINDOW, OpenUvEntity) TOPIC_UPDATE, TYPE_PROTECTION_WINDOW, OpenUvEntity)
from homeassistant.util.dt import as_local, parse_datetime, utcnow from homeassistant.util.dt import as_local, parse_datetime, utcnow
DEPENDENCIES = ['openuv'] DEPENDENCIES = ['openuv']
@ -26,17 +25,20 @@ ATTR_PROTECTION_WINDOW_ENDING_UV = 'end_uv'
async def async_setup_platform( async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None): hass, config, async_add_entities, discovery_info=None):
"""Set up the OpenUV binary sensor platform.""" """Set up an OpenUV sensor based on existing config."""
if discovery_info is None: pass
return
openuv = hass.data[DOMAIN]
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up an OpenUV sensor based on a config entry."""
openuv = hass.data[DOMAIN][DATA_OPENUV_CLIENT][entry.entry_id]
binary_sensors = [] binary_sensors = []
for sensor_type in discovery_info[CONF_MONITORED_CONDITIONS]: for sensor_type in openuv.binary_sensor_conditions:
name, icon = BINARY_SENSORS[sensor_type] name, icon = BINARY_SENSORS[sensor_type]
binary_sensors.append( binary_sensors.append(
OpenUvBinarySensor(openuv, sensor_type, name, icon)) OpenUvBinarySensor(
openuv, sensor_type, name, icon, entry.entry_id))
async_add_entities(binary_sensors, True) async_add_entities(binary_sensors, True)
@ -44,14 +46,16 @@ async def async_setup_platform(
class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice): class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice):
"""Define a binary sensor for OpenUV.""" """Define a binary sensor for OpenUV."""
def __init__(self, openuv, sensor_type, name, icon): def __init__(self, openuv, sensor_type, name, icon, entry_id):
"""Initialize the sensor.""" """Initialize the sensor."""
super().__init__(openuv) super().__init__(openuv)
self._entry_id = entry_id
self._icon = icon self._icon = icon
self._latitude = openuv.client.latitude self._latitude = openuv.client.latitude
self._longitude = openuv.client.longitude self._longitude = openuv.client.longitude
self._name = name self._name = name
self._dispatch_remove = None
self._sensor_type = sensor_type self._sensor_type = sensor_type
self._state = None self._state = None
@ -83,8 +87,9 @@ class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice):
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Register callbacks.""" """Register callbacks."""
async_dispatcher_connect( self._dispatch_remove = async_dispatcher_connect(
self.hass, TOPIC_UPDATE, self._update_data) self.hass, TOPIC_UPDATE, self._update_data)
self.async_on_remove(self._dispatch_remove)
async def async_update(self): async def async_update(self):
"""Update the state.""" """Update the state."""

View file

@ -0,0 +1,20 @@
{
"config": {
"error": {
"identifier_exists": "Coordinates already registered",
"invalid_api_key": "Invalid API key"
},
"step": {
"user": {
"data": {
"api_key": "OpenUV API Key",
"elevation": "Elevation",
"latitude": "Latitude",
"longitude": "Longitude"
},
"title": "Fill in your information"
}
},
"title": "OpenUV"
}
}

View file

@ -1,5 +1,5 @@
""" """
Support for data from openuv.io. Support for UV data from openuv.io.
For more details about this component, please refer to the documentation at For more details about this component, please refer to the documentation at
https://home-assistant.io/components/openuv/ https://home-assistant.io/components/openuv/
@ -9,21 +9,24 @@ from datetime import timedelta
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import ( from homeassistant.const import (
ATTR_ATTRIBUTION, CONF_API_KEY, CONF_BINARY_SENSORS, CONF_ELEVATION, ATTR_ATTRIBUTION, CONF_API_KEY, CONF_BINARY_SENSORS, CONF_ELEVATION,
CONF_LATITUDE, CONF_LONGITUDE, CONF_MONITORED_CONDITIONS, CONF_LATITUDE, CONF_LONGITUDE, CONF_MONITORED_CONDITIONS,
CONF_SCAN_INTERVAL, CONF_SENSORS) CONF_SCAN_INTERVAL, CONF_SENSORS)
from homeassistant.helpers import ( from homeassistant.helpers import aiohttp_client, config_validation as cv
aiohttp_client, config_validation as cv, discovery)
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.event import async_track_time_interval
REQUIREMENTS = ['pyopenuv==1.0.1'] from .config_flow import configured_instances
from .const import DOMAIN
REQUIREMENTS = ['pyopenuv==1.0.4']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DOMAIN = 'openuv' DATA_OPENUV_CLIENT = 'data_client'
DATA_OPENUV_LISTENER = 'data_listener'
DATA_PROTECTION_WINDOW = 'protection_window' DATA_PROTECTION_WINDOW = 'protection_window'
DATA_UV = 'uv' DATA_UV = 'uv'
@ -82,12 +85,14 @@ SENSOR_SCHEMA = vol.Schema({
}) })
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({ DOMAIN:
vol.Schema({
vol.Required(CONF_API_KEY): cv.string, vol.Required(CONF_API_KEY): cv.string,
vol.Optional(CONF_ELEVATION): float, vol.Optional(CONF_ELEVATION): float,
vol.Optional(CONF_LATITUDE): cv.latitude, vol.Optional(CONF_LATITUDE): cv.latitude,
vol.Optional(CONF_LONGITUDE): cv.longitude, vol.Optional(CONF_LONGITUDE): cv.longitude,
vol.Optional(CONF_BINARY_SENSORS, default={}): BINARY_SENSOR_SCHEMA, vol.Optional(CONF_BINARY_SENSORS, default={}):
BINARY_SENSOR_SCHEMA,
vol.Optional(CONF_SENSORS, default={}): SENSOR_SCHEMA, vol.Optional(CONF_SENSORS, default={}): SENSOR_SCHEMA,
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL):
cv.time_period, cv.time_period,
@ -97,24 +102,60 @@ CONFIG_SCHEMA = vol.Schema({
async def async_setup(hass, config): async def async_setup(hass, config):
"""Set up the OpenUV component.""" """Set up the OpenUV component."""
from pyopenuv import Client hass.data[DOMAIN] = {}
from pyopenuv.errors import OpenUvError hass.data[DOMAIN][DATA_OPENUV_CLIENT] = {}
hass.data[DOMAIN][DATA_OPENUV_LISTENER] = {}
if DOMAIN not in config:
return True
conf = config[DOMAIN] conf = config[DOMAIN]
api_key = conf[CONF_API_KEY]
elevation = conf.get(CONF_ELEVATION, hass.config.elevation)
latitude = conf.get(CONF_LATITUDE, hass.config.latitude) latitude = conf.get(CONF_LATITUDE, hass.config.latitude)
longitude = conf.get(CONF_LONGITUDE, hass.config.longitude) longitude = conf.get(CONF_LONGITUDE, hass.config.longitude)
elevation = conf.get(CONF_ELEVATION, hass.config.elevation)
identifier = '{0}, {1}'.format(latitude, longitude)
if identifier not in configured_instances(hass):
hass.async_add_job(
hass.config_entries.flow.async_init(
DOMAIN,
context={'source': SOURCE_IMPORT},
data={
CONF_API_KEY: conf[CONF_API_KEY],
CONF_LATITUDE: latitude,
CONF_LONGITUDE: longitude,
CONF_ELEVATION: elevation,
CONF_BINARY_SENSORS: conf[CONF_BINARY_SENSORS],
CONF_SENSORS: conf[CONF_SENSORS],
}))
hass.data[DOMAIN][CONF_SCAN_INTERVAL] = conf[CONF_SCAN_INTERVAL]
return True
async def async_setup_entry(hass, config_entry):
"""Set up OpenUV as config entry."""
from pyopenuv import Client
from pyopenuv.errors import OpenUvError
try: try:
websession = aiohttp_client.async_get_clientsession(hass) websession = aiohttp_client.async_get_clientsession(hass)
openuv = OpenUV( openuv = OpenUV(
Client( Client(
api_key, latitude, longitude, websession, altitude=elevation), config_entry.data[CONF_API_KEY],
conf[CONF_BINARY_SENSORS][CONF_MONITORED_CONDITIONS] + config_entry.data.get(CONF_LATITUDE, hass.config.latitude),
conf[CONF_SENSORS][CONF_MONITORED_CONDITIONS]) config_entry.data.get(CONF_LONGITUDE, hass.config.longitude),
websession,
altitude=config_entry.data.get(
CONF_ELEVATION, hass.config.elevation)),
config_entry.data.get(CONF_BINARY_SENSORS, {}).get(
CONF_MONITORED_CONDITIONS, list(BINARY_SENSORS)),
config_entry.data.get(CONF_SENSORS, {}).get(
CONF_MONITORED_CONDITIONS, list(SENSORS)))
await openuv.async_update() await openuv.async_update()
hass.data[DOMAIN] = openuv hass.data[DOMAIN][DATA_OPENUV_CLIENT][config_entry.entry_id] = openuv
except OpenUvError as err: except OpenUvError as err:
_LOGGER.error('An error occurred: %s', str(err)) _LOGGER.error('An error occurred: %s', str(err))
hass.components.persistent_notification.create( hass.components.persistent_notification.create(
@ -125,13 +166,9 @@ async def async_setup(hass, config):
notification_id=NOTIFICATION_ID) notification_id=NOTIFICATION_ID)
return False return False
for component, schema in [ for component in ('binary_sensor', 'sensor'):
('binary_sensor', conf[CONF_BINARY_SENSORS]), hass.async_create_task(hass.config_entries.async_forward_entry_setup(
('sensor', conf[CONF_SENSORS]), config_entry, component))
]:
hass.async_create_task(
discovery.async_load_platform(
hass, component, DOMAIN, schema, config))
async def refresh_sensors(event_time): async def refresh_sensors(event_time):
"""Refresh OpenUV data.""" """Refresh OpenUV data."""
@ -139,7 +176,25 @@ async def async_setup(hass, config):
await openuv.async_update() await openuv.async_update()
async_dispatcher_send(hass, TOPIC_UPDATE) async_dispatcher_send(hass, TOPIC_UPDATE)
async_track_time_interval(hass, refresh_sensors, conf[CONF_SCAN_INTERVAL]) hass.data[DOMAIN][DATA_OPENUV_LISTENER][
config_entry.entry_id] = async_track_time_interval(
hass, refresh_sensors,
hass.data[DOMAIN][CONF_SCAN_INTERVAL])
return True
async def async_unload_entry(hass, config_entry):
"""Unload an OpenUV config entry."""
for component in ('binary_sensor', 'sensor'):
await hass.config_entries.async_forward_entry_unload(
config_entry, component)
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()
return True return True
@ -147,19 +202,20 @@ async def async_setup(hass, config):
class OpenUV: class OpenUV:
"""Define a generic OpenUV object.""" """Define a generic OpenUV object."""
def __init__(self, client, monitored_conditions): def __init__(self, client, binary_sensor_conditions, sensor_conditions):
"""Initialize.""" """Initialize."""
self._monitored_conditions = monitored_conditions self.binary_sensor_conditions = binary_sensor_conditions
self.client = client self.client = client
self.data = {} self.data = {}
self.sensor_conditions = sensor_conditions
async def async_update(self): async def async_update(self):
"""Update sensor/binary sensor data.""" """Update sensor/binary sensor data."""
if TYPE_PROTECTION_WINDOW in self._monitored_conditions: if TYPE_PROTECTION_WINDOW in self.binary_sensor_conditions:
data = await self.client.uv_protection_window() data = await self.client.uv_protection_window()
self.data[DATA_PROTECTION_WINDOW] = data self.data[DATA_PROTECTION_WINDOW] = data
if any(c in self._monitored_conditions for c in SENSORS): if any(c in self.sensor_conditions for c in SENSORS):
data = await self.client.uv_index() data = await self.client.uv_index()
self.data[DATA_UV] = data self.data[DATA_UV] = data

View file

@ -0,0 +1,73 @@
"""Config flow to configure the OpenUV component."""
from collections import OrderedDict
import voluptuous as vol
from homeassistant import config_entries, data_entry_flow
from homeassistant.core import callback
from homeassistant.const import (
CONF_API_KEY, CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE)
from homeassistant.helpers import aiohttp_client, config_validation as cv
from .const import DOMAIN
@callback
def configured_instances(hass):
"""Return a set of configured OpenUV instances."""
return set(
'{0}, {1}'.format(
entry.data[CONF_LATITUDE], entry.data[CONF_LONGITUDE])
for entry in hass.config_entries.async_entries(DOMAIN))
@config_entries.HANDLERS.register(DOMAIN)
class OpenUvFlowHandler(data_entry_flow.FlowHandler):
"""Handle an OpenUV config flow."""
VERSION = 1
def __init__(self):
"""Initialize the config flow."""
pass
async def async_step_import(self, import_config):
"""Import a config entry from configuration.yaml."""
return await self.async_step_user(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
errors = {}
if user_input is not None:
identifier = '{0}, {1}'.format(
user_input.get(CONF_LATITUDE, self.hass.config.latitude),
user_input.get(CONF_LONGITUDE, self.hass.config.longitude))
if identifier in configured_instances(self.hass):
errors['base'] = 'identifier_exists'
else:
websession = aiohttp_client.async_get_clientsession(self.hass)
api_key_validation = await validate_api_key(
user_input[CONF_API_KEY], websession)
if api_key_validation:
return self.async_create_entry(
title=identifier,
data=user_input,
)
errors['base'] = 'invalid_api_key'
data_schema = OrderedDict()
data_schema[vol.Required(CONF_API_KEY)] = str
data_schema[vol.Optional(CONF_LATITUDE)] = cv.latitude
data_schema[vol.Optional(CONF_LONGITUDE)] = cv.longitude
data_schema[vol.Optional(CONF_ELEVATION)] = vol.Coerce(float)
return self.async_show_form(
step_id='user',
data_schema=vol.Schema(data_schema),
errors=errors,
)

View file

@ -0,0 +1,3 @@
"""Define constants for the OpenUV component."""
DOMAIN = 'openuv'

View file

@ -0,0 +1,20 @@
{
"config": {
"title": "OpenUV",
"step": {
"user": {
"title": "Fill in your information",
"data": {
"api_key": "OpenUV API Key",
"elevation": "Elevation",
"latitude": "Latitude",
"longitude": "Longitude"
}
}
},
"error": {
"identifier_exists": "Coordinates already registered",
"invalid_api_key": "Invalid API key"
}
}
}

View file

@ -6,13 +6,12 @@ https://home-assistant.io/components/sensor.openuv/
""" """
import logging import logging
from homeassistant.const import CONF_MONITORED_CONDITIONS
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.components.openuv import ( from homeassistant.components.openuv import (
DATA_UV, DOMAIN, SENSORS, TOPIC_UPDATE, TYPE_CURRENT_OZONE_LEVEL, DATA_OPENUV_CLIENT, DATA_UV, DOMAIN, SENSORS, TOPIC_UPDATE,
TYPE_CURRENT_UV_INDEX, TYPE_CURRENT_UV_LEVEL, TYPE_MAX_UV_INDEX, TYPE_CURRENT_OZONE_LEVEL, TYPE_CURRENT_UV_INDEX, TYPE_CURRENT_UV_LEVEL,
TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2, TYPE_MAX_UV_INDEX, TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2,
TYPE_SAFE_EXPOSURE_TIME_3, TYPE_SAFE_EXPOSURE_TIME_4, TYPE_SAFE_EXPOSURE_TIME_3, TYPE_SAFE_EXPOSURE_TIME_4,
TYPE_SAFE_EXPOSURE_TIME_5, TYPE_SAFE_EXPOSURE_TIME_6, OpenUvEntity) TYPE_SAFE_EXPOSURE_TIME_5, TYPE_SAFE_EXPOSURE_TIME_6, OpenUvEntity)
from homeassistant.util.dt import as_local, parse_datetime from homeassistant.util.dt import as_local, parse_datetime
@ -40,16 +39,20 @@ UV_LEVEL_LOW = "Low"
async def async_setup_platform( async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None): hass, config, async_add_entities, discovery_info=None):
"""Set up the OpenUV binary sensor platform.""" """Set up an OpenUV sensor based on existing config."""
if discovery_info is None: pass
return
openuv = hass.data[DOMAIN]
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up a Nest sensor based on a config entry."""
openuv = hass.data[DOMAIN][DATA_OPENUV_CLIENT][entry.entry_id]
sensors = [] sensors = []
for sensor_type in discovery_info[CONF_MONITORED_CONDITIONS]: for sensor_type in openuv.sensor_conditions:
name, icon, unit = SENSORS[sensor_type] name, icon, unit = SENSORS[sensor_type]
sensors.append(OpenUvSensor(openuv, sensor_type, name, icon, unit)) sensors.append(
OpenUvSensor(
openuv, sensor_type, name, icon, unit, entry.entry_id))
async_add_entities(sensors, True) async_add_entities(sensors, True)
@ -57,10 +60,12 @@ async def async_setup_platform(
class OpenUvSensor(OpenUvEntity): class OpenUvSensor(OpenUvEntity):
"""Define a binary sensor for OpenUV.""" """Define a binary sensor for OpenUV."""
def __init__(self, openuv, sensor_type, name, icon, unit): def __init__(self, openuv, sensor_type, name, icon, unit, entry_id):
"""Initialize the sensor.""" """Initialize the sensor."""
super().__init__(openuv) super().__init__(openuv)
self._dispatch_remove = None
self._entry_id = entry_id
self._icon = icon self._icon = icon
self._latitude = openuv.client.latitude self._latitude = openuv.client.latitude
self._longitude = openuv.client.longitude self._longitude = openuv.client.longitude
@ -102,7 +107,9 @@ class OpenUvSensor(OpenUvEntity):
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Register callbacks.""" """Register callbacks."""
async_dispatcher_connect(self.hass, TOPIC_UPDATE, self._update_data) self._dispatch_remove = async_dispatcher_connect(
self.hass, TOPIC_UPDATE, self._update_data)
self.async_on_remove(self._dispatch_remove)
async def async_update(self): async def async_update(self):
"""Update the state.""" """Update the state."""
@ -125,8 +132,7 @@ class OpenUvSensor(OpenUvEntity):
elif self._sensor_type == TYPE_MAX_UV_INDEX: elif self._sensor_type == TYPE_MAX_UV_INDEX:
self._state = data['uv_max'] self._state = data['uv_max']
self._attrs.update({ self._attrs.update({
ATTR_MAX_UV_TIME: as_local( ATTR_MAX_UV_TIME: as_local(parse_datetime(data['uv_max_time']))
parse_datetime(data['uv_max_time']))
}) })
elif self._sensor_type in (TYPE_SAFE_EXPOSURE_TIME_1, elif self._sensor_type in (TYPE_SAFE_EXPOSURE_TIME_1,
TYPE_SAFE_EXPOSURE_TIME_2, TYPE_SAFE_EXPOSURE_TIME_2,

View file

@ -141,6 +141,7 @@ FLOWS = [
'homematicip_cloud', 'homematicip_cloud',
'hue', 'hue',
'nest', 'nest',
'openuv',
'sonos', 'sonos',
'zone', 'zone',
] ]

View file

@ -990,7 +990,7 @@ pynut2==2.1.2
pynx584==0.4 pynx584==0.4
# homeassistant.components.openuv # homeassistant.components.openuv
pyopenuv==1.0.1 pyopenuv==1.0.4
# homeassistant.components.iota # homeassistant.components.iota
pyota==2.0.5 pyota==2.0.5

View file

@ -154,6 +154,9 @@ pymonoprice==0.3
# homeassistant.components.binary_sensor.nx584 # homeassistant.components.binary_sensor.nx584
pynx584==0.4 pynx584==0.4
# homeassistant.components.openuv
pyopenuv==1.0.4
# homeassistant.auth.mfa_modules.totp # homeassistant.auth.mfa_modules.totp
# homeassistant.components.sensor.otp # homeassistant.components.sensor.otp
pyotp==2.2.6 pyotp==2.2.6

View file

@ -78,6 +78,7 @@ TEST_REQUIREMENTS = (
'pylitejet', 'pylitejet',
'pymonoprice', 'pymonoprice',
'pynx584', 'pynx584',
'pyopenuv',
'pyotp', 'pyotp',
'pyqwikswitch', 'pyqwikswitch',
'PyRMVtransport', 'PyRMVtransport',

View file

@ -0,0 +1 @@
"""Define tests for the OpenUV component."""

View file

@ -0,0 +1,93 @@
"""Define tests for the OpenUV config flow."""
from unittest.mock import patch
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)
from tests.common import MockConfigEntry, mock_coro
async def test_duplicate_error(hass):
"""Test that errors are shown when duplicates are added."""
conf = {
CONF_API_KEY: '12345abcde',
CONF_ELEVATION: 59.1234,
CONF_LATITUDE: 39.128712,
CONF_LONGITUDE: -104.9812612,
}
MockConfigEntry(domain=DOMAIN, data=conf).add_to_hass(hass)
flow = config_flow.OpenUvFlowHandler()
flow.hass = hass
result = await flow.async_step_user(user_input=conf)
assert result['errors'] == {'base': 'identifier_exists'}
@patch('pyopenuv.util.validate_api_key', return_value=mock_coro(False))
async def test_invalid_api_key(hass):
"""Test that an invalid API key throws an error."""
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_user(user_input=conf)
assert result['errors'] == {'base': 'invalid_api_key'}
async def test_show_form(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'
@patch('pyopenuv.util.validate_api_key', return_value=mock_coro(True))
async def test_step_import(hass):
"""Test that the import step works."""
conf = {
CONF_API_KEY: '12345abcde',
}
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'] == '{0}, {1}'.format(
hass.config.latitude, hass.config.longitude)
assert result['data'] == conf
@patch('pyopenuv.util.validate_api_key', return_value=mock_coro(True))
async def test_step_user(hass):
"""Test that the user 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_user(user_input=conf)
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result['title'] == '{0}, {1}'.format(
conf[CONF_LATITUDE], conf[CONF_LONGITUDE])
assert result['data'] == conf