From 15645ab0c9a8dfa9538b172ee4d3a993cdab2614 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Mon, 13 Jan 2020 13:28:07 +0100 Subject: [PATCH] Add unique ID to Airly config entries (#30681) * Add unique ID to Airly config entries * Update tests * Update tests * Fix typo * Remove unnecesary and undo changes in first test * Suggested change --- homeassistant/components/airly/__init__.py | 6 +++ homeassistant/components/airly/air_quality.py | 7 +--- homeassistant/components/airly/config_flow.py | 21 ++++------ homeassistant/components/airly/sensor.py | 6 +-- homeassistant/components/airly/strings.json | 4 +- tests/components/airly/test_config_flow.py | 42 ++++++++++--------- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/airly/__init__.py b/homeassistant/components/airly/__init__.py index 17e1d27e571..bad5a48c05f 100644 --- a/homeassistant/components/airly/__init__.py +++ b/homeassistant/components/airly/__init__.py @@ -41,6 +41,12 @@ async def async_setup_entry(hass, config_entry): latitude = config_entry.data[CONF_LATITUDE] longitude = config_entry.data[CONF_LONGITUDE] + # For backwards compat, set unique ID + if config_entry.unique_id is None: + hass.config_entries.async_update_entry( + config_entry, unique_id=f"{latitude}-{longitude}" + ) + websession = async_get_clientsession(hass) airly = AirlyData(websession, api_key, latitude, longitude) diff --git a/homeassistant/components/airly/air_quality.py b/homeassistant/components/airly/air_quality.py index b48a360da28..45b4dfa3a37 100644 --- a/homeassistant/components/airly/air_quality.py +++ b/homeassistant/components/airly/air_quality.py @@ -5,7 +5,7 @@ from homeassistant.components.air_quality import ( ATTR_PM_10, AirQualityEntity, ) -from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME +from homeassistant.const import CONF_NAME from .const import ( ATTR_API_ADVICE, @@ -35,13 +35,10 @@ LABEL_PM_10_PERCENT = f"{ATTR_PM_10}_percent_of_limit" async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Airly air_quality entity based on a config entry.""" name = config_entry.data[CONF_NAME] - latitude = config_entry.data[CONF_LATITUDE] - longitude = config_entry.data[CONF_LONGITUDE] - unique_id = f"{latitude}-{longitude}" data = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] - async_add_entities([AirlyAirQuality(data, name, unique_id)], True) + async_add_entities([AirlyAirQuality(data, name, config_entry.unique_id)], True) def round_state(func): diff --git a/homeassistant/components/airly/config_flow.py b/homeassistant/components/airly/config_flow.py index 31cfec7e7aa..84bad2d3719 100644 --- a/homeassistant/components/airly/config_flow.py +++ b/homeassistant/components/airly/config_flow.py @@ -6,19 +6,14 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME -from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from .const import DEFAULT_NAME, DOMAIN, NO_AIRLY_SENSORS - - -@callback -def configured_instances(hass): - """Return a set of configured Airly instances.""" - return set( - entry.data[CONF_NAME] for entry in hass.config_entries.async_entries(DOMAIN) - ) +from .const import ( # pylint:disable=unused-import + DEFAULT_NAME, + DOMAIN, + NO_AIRLY_SENSORS, +) class AirlyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @@ -38,8 +33,10 @@ class AirlyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): websession = async_get_clientsession(self.hass) if user_input is not None: - if user_input[CONF_NAME] in configured_instances(self.hass): - self._errors[CONF_NAME] = "name_exists" + await self.async_set_unique_id( + f"{user_input[CONF_LATITUDE]}-{user_input[CONF_LONGITUDE]}" + ) + self._abort_if_unique_id_configured() api_key_valid = await self._test_api_key(websession, user_input["api_key"]) if not api_key_valid: self._errors["base"] = "auth" diff --git a/homeassistant/components/airly/sensor.py b/homeassistant/components/airly/sensor.py index af0eac39cdc..ab83f711153 100644 --- a/homeassistant/components/airly/sensor.py +++ b/homeassistant/components/airly/sensor.py @@ -2,8 +2,6 @@ from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_DEVICE_CLASS, - CONF_LATITUDE, - CONF_LONGITUDE, CONF_NAME, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, @@ -62,14 +60,12 @@ SENSOR_TYPES = { async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Airly sensor entities based on a config entry.""" name = config_entry.data[CONF_NAME] - latitude = config_entry.data[CONF_LATITUDE] - longitude = config_entry.data[CONF_LONGITUDE] data = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] sensors = [] for sensor in SENSOR_TYPES: - unique_id = f"{latitude}-{longitude}-{sensor.lower()}" + unique_id = f"{config_entry.unique_id}-{sensor.lower()}" sensors.append(AirlySensor(data, name, sensor, unique_id)) async_add_entities(sensors, True) diff --git a/homeassistant/components/airly/strings.json b/homeassistant/components/airly/strings.json index 116b6df83e6..d8047265415 100644 --- a/homeassistant/components/airly/strings.json +++ b/homeassistant/components/airly/strings.json @@ -14,9 +14,11 @@ } }, "error": { - "name_exists": "Name already exists.", "wrong_location": "No Airly measuring stations in this area.", "auth": "API key is not correct." + }, + "abort": { + "already_configured": "Airly integration for these coordinates is already configured." } } } diff --git a/tests/components/airly/test_config_flow.py b/tests/components/airly/test_config_flow.py index a5ca3981a5a..1f14e96ed37 100644 --- a/tests/components/airly/test_config_flow.py +++ b/tests/components/airly/test_config_flow.py @@ -5,8 +5,8 @@ from airly.exceptions import AirlyError from asynctest import patch from homeassistant import data_entry_flow -from homeassistant.components.airly import config_flow from homeassistant.components.airly.const import DOMAIN +from homeassistant.config_entries import SOURCE_USER from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME from tests.common import MockConfigEntry, load_fixture @@ -21,13 +21,12 @@ CONFIG = { async def test_show_form(hass): """Test that the form is served with no input.""" - flow = config_flow.AirlyFlowHandler() - flow.hass = hass - - result = await flow.async_step_user(user_input=None) + 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" + assert result["step_id"] == SOURCE_USER async def test_invalid_api_key(hass): @@ -36,10 +35,10 @@ async def test_invalid_api_key(hass): "airly._private._RequestsHandler.get", side_effect=AirlyError(403, {"message": "Invalid authentication credentials"}), ): - flow = config_flow.AirlyFlowHandler() - flow.hass = hass - result = await flow.async_step_user(user_input=CONFIG) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=CONFIG + ) assert result["errors"] == {"base": "auth"} @@ -50,10 +49,10 @@ async def test_invalid_location(hass): "airly._private._RequestsHandler.get", return_value=json.loads(load_fixture("airly_no_station.json")), ): - flow = config_flow.AirlyFlowHandler() - flow.hass = hass - result = await flow.async_step_user(user_input=CONFIG) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=CONFIG + ) assert result["errors"] == {"base": "wrong_location"} @@ -65,13 +64,16 @@ async def test_duplicate_error(hass): "airly._private._RequestsHandler.get", return_value=json.loads(load_fixture("airly_valid_station.json")), ): - MockConfigEntry(domain=DOMAIN, data=CONFIG).add_to_hass(hass) - flow = config_flow.AirlyFlowHandler() - flow.hass = hass + MockConfigEntry(domain=DOMAIN, unique_id="123-456", data=CONFIG).add_to_hass( + hass + ) - result = await flow.async_step_user(user_input=CONFIG) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=CONFIG + ) - assert result["errors"] == {CONF_NAME: "name_exists"} + assert result["type"] == "abort" + assert result["reason"] == "already_configured" async def test_create_entry(hass): @@ -81,10 +83,10 @@ async def test_create_entry(hass): "airly._private._RequestsHandler.get", return_value=json.loads(load_fixture("airly_valid_station.json")), ): - flow = config_flow.AirlyFlowHandler() - flow.hass = hass - result = await flow.async_step_user(user_input=CONFIG) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=CONFIG + ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == CONFIG[CONF_NAME]