From f96aee2832b8834e48c24ccd54c3d0218614a641 Mon Sep 17 00:00:00 2001 From: Aaron Bach <bachya1208@gmail.com> Date: Tue, 4 Sep 2018 01:22:44 -0600 Subject: [PATCH] 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 --- .coveragerc | 8 +- .../components/binary_sensor/openuv.py | 27 ++-- .../components/openuv/.translations/en.json | 20 +++ .../{openuv.py => openuv/__init__.py} | 128 +++++++++++++----- .../components/openuv/config_flow.py | 73 ++++++++++ homeassistant/components/openuv/const.py | 3 + homeassistant/components/openuv/strings.json | 20 +++ homeassistant/components/sensor/openuv.py | 34 +++-- homeassistant/config_entries.py | 1 + requirements_all.txt | 2 +- requirements_test_all.txt | 3 + script/gen_requirements_all.py | 1 + tests/components/openuv/__init__.py | 1 + tests/components/openuv/test_config_flow.py | 93 +++++++++++++ 14 files changed, 348 insertions(+), 66 deletions(-) create mode 100644 homeassistant/components/openuv/.translations/en.json rename homeassistant/components/{openuv.py => openuv/__init__.py} (57%) create mode 100644 homeassistant/components/openuv/config_flow.py create mode 100644 homeassistant/components/openuv/const.py create mode 100644 homeassistant/components/openuv/strings.json create mode 100644 tests/components/openuv/__init__.py create mode 100644 tests/components/openuv/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 39c31e4e40b..bd531e62f72 100644 --- a/.coveragerc +++ b/.coveragerc @@ -123,7 +123,7 @@ omit = homeassistant/components/hangouts/const.py homeassistant/components/hangouts/hangouts_bot.py homeassistant/components/hangouts/hangups_utils.py - homeassistant/components/*/hangouts.py + homeassistant/components/*/hangouts.py homeassistant/components/hdmi_cec.py homeassistant/components/*/hdmi_cec.py @@ -145,12 +145,12 @@ omit = homeassistant/components/ihc/* homeassistant/components/*/ihc.py - + homeassistant/components/insteon/* homeassistant/components/*/insteon.py homeassistant/components/insteon_local.py - + homeassistant/components/insteon_plm.py homeassistant/components/ios.py @@ -228,7 +228,7 @@ omit = homeassistant/components/opencv.py homeassistant/components/*/opencv.py - homeassistant/components/openuv.py + homeassistant/components/openuv/__init__.py homeassistant/components/*/openuv.py homeassistant/components/pilight.py diff --git a/homeassistant/components/binary_sensor/openuv.py b/homeassistant/components/binary_sensor/openuv.py index 0b299529a46..c7c27d73ee4 100644 --- a/homeassistant/components/binary_sensor/openuv.py +++ b/homeassistant/components/binary_sensor/openuv.py @@ -7,12 +7,11 @@ https://home-assistant.io/components/binary_sensor.openuv/ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.openuv import ( - BINARY_SENSORS, DATA_PROTECTION_WINDOW, DOMAIN, TOPIC_UPDATE, - TYPE_PROTECTION_WINDOW, OpenUvEntity) + BINARY_SENSORS, DATA_OPENUV_CLIENT, DATA_PROTECTION_WINDOW, DOMAIN, + TOPIC_UPDATE, TYPE_PROTECTION_WINDOW, OpenUvEntity) from homeassistant.util.dt import as_local, parse_datetime, utcnow DEPENDENCIES = ['openuv'] @@ -26,17 +25,20 @@ ATTR_PROTECTION_WINDOW_ENDING_UV = 'end_uv' async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): - """Set up the OpenUV binary sensor platform.""" - if discovery_info is None: - return + """Set up an OpenUV sensor based on existing config.""" + pass - 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 = [] - for sensor_type in discovery_info[CONF_MONITORED_CONDITIONS]: + for sensor_type in openuv.binary_sensor_conditions: name, icon = BINARY_SENSORS[sensor_type] binary_sensors.append( - OpenUvBinarySensor(openuv, sensor_type, name, icon)) + OpenUvBinarySensor( + openuv, sensor_type, name, icon, entry.entry_id)) async_add_entities(binary_sensors, True) @@ -44,14 +46,16 @@ async def async_setup_platform( class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice): """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.""" super().__init__(openuv) + self._entry_id = entry_id self._icon = icon self._latitude = openuv.client.latitude self._longitude = openuv.client.longitude self._name = name + self._dispatch_remove = None self._sensor_type = sensor_type self._state = None @@ -83,8 +87,9 @@ class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice): async def async_added_to_hass(self): """Register callbacks.""" - async_dispatcher_connect( + 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): """Update the state.""" diff --git a/homeassistant/components/openuv/.translations/en.json b/homeassistant/components/openuv/.translations/en.json new file mode 100644 index 00000000000..df0232d01fc --- /dev/null +++ b/homeassistant/components/openuv/.translations/en.json @@ -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" + } +} \ No newline at end of file diff --git a/homeassistant/components/openuv.py b/homeassistant/components/openuv/__init__.py similarity index 57% rename from homeassistant/components/openuv.py rename to homeassistant/components/openuv/__init__.py index d696f0e5100..bfd90b4a574 100644 --- a/homeassistant/components/openuv.py +++ b/homeassistant/components/openuv/__init__.py @@ -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 https://home-assistant.io/components/openuv/ @@ -9,21 +9,24 @@ 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) -from homeassistant.helpers import ( - aiohttp_client, config_validation as cv, discovery) +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 -REQUIREMENTS = ['pyopenuv==1.0.1'] -_LOGGER = logging.getLogger(__name__) +from .config_flow import configured_instances +from .const import DOMAIN -DOMAIN = 'openuv' +REQUIREMENTS = ['pyopenuv==1.0.4'] +_LOGGER = logging.getLogger(__name__) +DATA_OPENUV_CLIENT = 'data_client' +DATA_OPENUV_LISTENER = 'data_listener' DATA_PROTECTION_WINDOW = 'protection_window' DATA_UV = 'uv' @@ -82,39 +85,77 @@ SENSOR_SCHEMA = vol.Schema({ }) CONFIG_SCHEMA = vol.Schema({ - 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, - 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, - }) + 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, + 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) async def async_setup(hass, config): """Set up the OpenUV component.""" - from pyopenuv import Client - from pyopenuv.errors import OpenUvError + hass.data[DOMAIN] = {} + hass.data[DOMAIN][DATA_OPENUV_CLIENT] = {} + hass.data[DOMAIN][DATA_OPENUV_LISTENER] = {} + + if DOMAIN not in config: + return True 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) 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: websession = aiohttp_client.async_get_clientsession(hass) openuv = OpenUV( Client( - api_key, latitude, longitude, websession, altitude=elevation), - conf[CONF_BINARY_SENSORS][CONF_MONITORED_CONDITIONS] + - conf[CONF_SENSORS][CONF_MONITORED_CONDITIONS]) + config_entry.data[CONF_API_KEY], + config_entry.data.get(CONF_LATITUDE, hass.config.latitude), + 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() - hass.data[DOMAIN] = openuv + hass.data[DOMAIN][DATA_OPENUV_CLIENT][config_entry.entry_id] = openuv except OpenUvError as err: _LOGGER.error('An error occurred: %s', str(err)) hass.components.persistent_notification.create( @@ -125,13 +166,9 @@ async def async_setup(hass, config): notification_id=NOTIFICATION_ID) return False - for component, schema in [ - ('binary_sensor', conf[CONF_BINARY_SENSORS]), - ('sensor', conf[CONF_SENSORS]), - ]: - hass.async_create_task( - discovery.async_load_platform( - hass, component, DOMAIN, schema, config)) + for component in ('binary_sensor', 'sensor'): + hass.async_create_task(hass.config_entries.async_forward_entry_setup( + config_entry, component)) async def refresh_sensors(event_time): """Refresh OpenUV data.""" @@ -139,7 +176,25 @@ async def async_setup(hass, config): await openuv.async_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 @@ -147,19 +202,20 @@ async def async_setup(hass, config): class OpenUV: """Define a generic OpenUV object.""" - def __init__(self, client, monitored_conditions): + def __init__(self, client, binary_sensor_conditions, sensor_conditions): """Initialize.""" - self._monitored_conditions = monitored_conditions + self.binary_sensor_conditions = binary_sensor_conditions self.client = client self.data = {} + self.sensor_conditions = sensor_conditions async def async_update(self): """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() 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() self.data[DATA_UV] = data diff --git a/homeassistant/components/openuv/config_flow.py b/homeassistant/components/openuv/config_flow.py new file mode 100644 index 00000000000..55ee566268e --- /dev/null +++ b/homeassistant/components/openuv/config_flow.py @@ -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, + ) diff --git a/homeassistant/components/openuv/const.py b/homeassistant/components/openuv/const.py new file mode 100644 index 00000000000..1aa3d2abcaa --- /dev/null +++ b/homeassistant/components/openuv/const.py @@ -0,0 +1,3 @@ +"""Define constants for the OpenUV component.""" + +DOMAIN = 'openuv' diff --git a/homeassistant/components/openuv/strings.json b/homeassistant/components/openuv/strings.json new file mode 100644 index 00000000000..9c5af45619e --- /dev/null +++ b/homeassistant/components/openuv/strings.json @@ -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" + } + } +} diff --git a/homeassistant/components/sensor/openuv.py b/homeassistant/components/sensor/openuv.py index aaa04590b3f..22712aa306b 100644 --- a/homeassistant/components/sensor/openuv.py +++ b/homeassistant/components/sensor/openuv.py @@ -6,13 +6,12 @@ https://home-assistant.io/components/sensor.openuv/ """ import logging -from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.openuv import ( - DATA_UV, DOMAIN, SENSORS, TOPIC_UPDATE, TYPE_CURRENT_OZONE_LEVEL, - TYPE_CURRENT_UV_INDEX, TYPE_CURRENT_UV_LEVEL, TYPE_MAX_UV_INDEX, - TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2, + DATA_OPENUV_CLIENT, DATA_UV, DOMAIN, SENSORS, TOPIC_UPDATE, + TYPE_CURRENT_OZONE_LEVEL, TYPE_CURRENT_UV_INDEX, TYPE_CURRENT_UV_LEVEL, + 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_5, TYPE_SAFE_EXPOSURE_TIME_6, OpenUvEntity) from homeassistant.util.dt import as_local, parse_datetime @@ -40,16 +39,20 @@ UV_LEVEL_LOW = "Low" async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): - """Set up the OpenUV binary sensor platform.""" - if discovery_info is None: - return + """Set up an OpenUV sensor based on existing config.""" + pass - 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 = [] - for sensor_type in discovery_info[CONF_MONITORED_CONDITIONS]: + for sensor_type in openuv.sensor_conditions: 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) @@ -57,10 +60,12 @@ async def async_setup_platform( class OpenUvSensor(OpenUvEntity): """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.""" super().__init__(openuv) + self._dispatch_remove = None + self._entry_id = entry_id self._icon = icon self._latitude = openuv.client.latitude self._longitude = openuv.client.longitude @@ -102,7 +107,9 @@ class OpenUvSensor(OpenUvEntity): async def async_added_to_hass(self): """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): """Update the state.""" @@ -125,8 +132,7 @@ class OpenUvSensor(OpenUvEntity): elif self._sensor_type == TYPE_MAX_UV_INDEX: self._state = data['uv_max'] self._attrs.update({ - ATTR_MAX_UV_TIME: as_local( - parse_datetime(data['uv_max_time'])) + ATTR_MAX_UV_TIME: as_local(parse_datetime(data['uv_max_time'])) }) elif self._sensor_type in (TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2, diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 6eae9e13030..15932f2c3f8 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -141,6 +141,7 @@ FLOWS = [ 'homematicip_cloud', 'hue', 'nest', + 'openuv', 'sonos', 'zone', ] diff --git a/requirements_all.txt b/requirements_all.txt index d8d73cc36a1..b155612350b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -990,7 +990,7 @@ pynut2==2.1.2 pynx584==0.4 # homeassistant.components.openuv -pyopenuv==1.0.1 +pyopenuv==1.0.4 # homeassistant.components.iota pyota==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0ee02e0d109..b9e44445114 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -154,6 +154,9 @@ pymonoprice==0.3 # homeassistant.components.binary_sensor.nx584 pynx584==0.4 +# homeassistant.components.openuv +pyopenuv==1.0.4 + # homeassistant.auth.mfa_modules.totp # homeassistant.components.sensor.otp pyotp==2.2.6 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 4b694ec7ec0..fc8e67b1ab6 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -78,6 +78,7 @@ TEST_REQUIREMENTS = ( 'pylitejet', 'pymonoprice', 'pynx584', + 'pyopenuv', 'pyotp', 'pyqwikswitch', 'PyRMVtransport', diff --git a/tests/components/openuv/__init__.py b/tests/components/openuv/__init__.py new file mode 100644 index 00000000000..0e3595b1e51 --- /dev/null +++ b/tests/components/openuv/__init__.py @@ -0,0 +1 @@ +"""Define tests for the OpenUV component.""" diff --git a/tests/components/openuv/test_config_flow.py b/tests/components/openuv/test_config_flow.py new file mode 100644 index 00000000000..0e50bddabde --- /dev/null +++ b/tests/components/openuv/test_config_flow.py @@ -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 -- GitLab