Add unique id to UniFi config entries using the unique id of the site it is controlling (#45737)

* Add unique id to UniFi config entries using the unique id of the site it is controlling

* Fix failing test
pull/46073/head
Robert Svensson 2021-02-05 19:38:08 +01:00 committed by GitHub
parent 55f9d98523
commit 0d620eb7c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 61 additions and 37 deletions

View File

@ -32,6 +32,11 @@ async def async_setup_entry(hass, config_entry):
if not await controller.async_setup(): if not await controller.async_setup():
return False return False
if config_entry.unique_id is None:
hass.config_entries.async_update_entry(
config_entry, unique_id=controller.site_id
)
hass.data[UNIFI_DOMAIN][config_entry.entry_id] = controller hass.data[UNIFI_DOMAIN][config_entry.entry_id] = controller
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, controller.shutdown) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, controller.shutdown)

View File

@ -70,7 +70,8 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN):
def __init__(self): def __init__(self):
"""Initialize the UniFi flow.""" """Initialize the UniFi flow."""
self.config = {} self.config = {}
self.sites = None self.site_ids = {}
self.site_names = {}
self.reauth_config_entry = None self.reauth_config_entry = None
self.reauth_config = {} self.reauth_config = {}
self.reauth_schema = {} self.reauth_schema = {}
@ -101,11 +102,15 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN):
errors["base"] = "service_unavailable" errors["base"] = "service_unavailable"
else: else:
self.sites = {site["name"]: site["desc"] for site in sites.values()} self.site_ids = {site["_id"]: site["name"] for site in sites.values()}
self.site_names = {site["_id"]: site["desc"] for site in sites.values()}
if self.reauth_config.get(CONF_SITE_ID) in self.sites: if (
self.reauth_config_entry
and self.reauth_config_entry.unique_id in self.site_names
):
return await self.async_step_site( return await self.async_step_site(
{CONF_SITE_ID: self.reauth_config[CONF_SITE_ID]} {CONF_SITE_ID: self.reauth_config_entry.unique_id}
) )
return await self.async_step_site() return await self.async_step_site()
@ -136,26 +141,18 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN):
if user_input is not None: if user_input is not None:
self.config[CONF_SITE_ID] = user_input[CONF_SITE_ID] unique_id = user_input[CONF_SITE_ID]
self.config[CONF_SITE_ID] = self.site_ids[unique_id]
data = {CONF_CONTROLLER: self.config} data = {CONF_CONTROLLER: self.config}
config_entry = await self.async_set_unique_id(unique_id)
abort_reason = "configuration_updated"
if self.reauth_config_entry: if self.reauth_config_entry:
self.hass.config_entries.async_update_entry( config_entry = self.reauth_config_entry
self.reauth_config_entry, data=data abort_reason = "reauth_successful"
)
await self.hass.config_entries.async_reload(
self.reauth_config_entry.entry_id
)
return self.async_abort(reason="reauth_successful")
for config_entry in self._async_current_entries():
controller_data = config_entry.data[CONF_CONTROLLER]
if (
controller_data[CONF_HOST] != self.config[CONF_HOST]
or controller_data[CONF_SITE_ID] != self.config[CONF_SITE_ID]
):
continue
if config_entry:
controller = self.hass.data.get(UNIFI_DOMAIN, {}).get( controller = self.hass.data.get(UNIFI_DOMAIN, {}).get(
config_entry.entry_id config_entry.entry_id
) )
@ -165,17 +162,21 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN):
self.hass.config_entries.async_update_entry(config_entry, data=data) self.hass.config_entries.async_update_entry(config_entry, data=data)
await self.hass.config_entries.async_reload(config_entry.entry_id) await self.hass.config_entries.async_reload(config_entry.entry_id)
return self.async_abort(reason="configuration_updated") return self.async_abort(reason=abort_reason)
site_nice_name = self.sites[self.config[CONF_SITE_ID]] site_nice_name = self.site_names[unique_id]
return self.async_create_entry(title=site_nice_name, data=data) return self.async_create_entry(title=site_nice_name, data=data)
if len(self.sites) == 1: if len(self.site_names) == 1:
return await self.async_step_site({CONF_SITE_ID: next(iter(self.sites))}) return await self.async_step_site(
{CONF_SITE_ID: next(iter(self.site_names))}
)
return self.async_show_form( return self.async_show_form(
step_id="site", step_id="site",
data_schema=vol.Schema({vol.Required(CONF_SITE_ID): vol.In(self.sites)}), data_schema=vol.Schema(
{vol.Required(CONF_SITE_ID): vol.In(self.site_names)}
),
errors=errors, errors=errors,
) )

View File

@ -95,6 +95,7 @@ class UniFiController:
self.wireless_clients = None self.wireless_clients = None
self.listeners = [] self.listeners = []
self.site_id: str = ""
self._site_name = None self._site_name = None
self._site_role = None self._site_role = None
@ -321,6 +322,7 @@ class UniFiController:
for site in sites.values(): for site in sites.values():
if self.site == site["name"]: if self.site == site["name"]:
self.site_id = site["_id"]
self._site_name = site["desc"] self._site_name = site["desc"]
break break

View File

@ -112,7 +112,9 @@ async def test_flow_works(hass, aioclient_mock, mock_discovery):
aioclient_mock.get( aioclient_mock.get(
"https://1.2.3.4:1234/api/self/sites", "https://1.2.3.4:1234/api/self/sites",
json={ json={
"data": [{"desc": "Site name", "name": "site_id", "role": "admin"}], "data": [
{"desc": "Site name", "name": "site_id", "role": "admin", "_id": "1"}
],
"meta": {"rc": "ok"}, "meta": {"rc": "ok"},
}, },
headers={"content-type": CONTENT_TYPE_JSON}, headers={"content-type": CONTENT_TYPE_JSON},
@ -143,7 +145,7 @@ async def test_flow_works(hass, aioclient_mock, mock_discovery):
} }
async def test_flow_works_multiple_sites(hass, aioclient_mock): async def test_flow_multiple_sites(hass, aioclient_mock):
"""Test config flow works when finding multiple sites.""" """Test config flow works when finding multiple sites."""
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
UNIFI_DOMAIN, context={"source": "user"} UNIFI_DOMAIN, context={"source": "user"}
@ -164,8 +166,8 @@ async def test_flow_works_multiple_sites(hass, aioclient_mock):
"https://1.2.3.4:1234/api/self/sites", "https://1.2.3.4:1234/api/self/sites",
json={ json={
"data": [ "data": [
{"name": "default", "role": "admin", "desc": "site name"}, {"name": "default", "role": "admin", "desc": "site name", "_id": "1"},
{"name": "site2", "role": "admin", "desc": "site2 name"}, {"name": "site2", "role": "admin", "desc": "site2 name", "_id": "2"},
], ],
"meta": {"rc": "ok"}, "meta": {"rc": "ok"},
}, },
@ -185,8 +187,8 @@ async def test_flow_works_multiple_sites(hass, aioclient_mock):
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "site" assert result["step_id"] == "site"
assert result["data_schema"]({"site": "default"}) assert result["data_schema"]({"site": "1"})
assert result["data_schema"]({"site": "site2"}) assert result["data_schema"]({"site": "2"})
async def test_flow_raise_already_configured(hass, aioclient_mock): async def test_flow_raise_already_configured(hass, aioclient_mock):
@ -213,7 +215,9 @@ async def test_flow_raise_already_configured(hass, aioclient_mock):
aioclient_mock.get( aioclient_mock.get(
"https://1.2.3.4:1234/api/self/sites", "https://1.2.3.4:1234/api/self/sites",
json={ json={
"data": [{"desc": "Site name", "name": "site_id", "role": "admin"}], "data": [
{"desc": "Site name", "name": "site_id", "role": "admin", "_id": "1"}
],
"meta": {"rc": "ok"}, "meta": {"rc": "ok"},
}, },
headers={"content-type": CONTENT_TYPE_JSON}, headers={"content-type": CONTENT_TYPE_JSON},
@ -237,12 +241,16 @@ async def test_flow_raise_already_configured(hass, aioclient_mock):
async def test_flow_aborts_configuration_updated(hass, aioclient_mock): async def test_flow_aborts_configuration_updated(hass, aioclient_mock):
"""Test config flow aborts since a connected config entry already exists.""" """Test config flow aborts since a connected config entry already exists."""
entry = MockConfigEntry( entry = MockConfigEntry(
domain=UNIFI_DOMAIN, data={"controller": {"host": "1.2.3.4", "site": "office"}} domain=UNIFI_DOMAIN,
data={"controller": {"host": "1.2.3.4", "site": "office"}},
unique_id="2",
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
entry = MockConfigEntry( entry = MockConfigEntry(
domain=UNIFI_DOMAIN, data={"controller": {"host": "1.2.3.4", "site": "site_id"}} domain=UNIFI_DOMAIN,
data={"controller": {"host": "1.2.3.4", "site": "site_id"}},
unique_id="1",
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
@ -264,7 +272,9 @@ async def test_flow_aborts_configuration_updated(hass, aioclient_mock):
aioclient_mock.get( aioclient_mock.get(
"https://1.2.3.4:1234/api/self/sites", "https://1.2.3.4:1234/api/self/sites",
json={ json={
"data": [{"desc": "Site name", "name": "site_id", "role": "admin"}], "data": [
{"desc": "Site name", "name": "site_id", "role": "admin", "_id": "1"}
],
"meta": {"rc": "ok"}, "meta": {"rc": "ok"},
}, },
headers={"content-type": CONTENT_TYPE_JSON}, headers={"content-type": CONTENT_TYPE_JSON},
@ -343,6 +353,8 @@ async def test_flow_fails_controller_unavailable(hass, aioclient_mock):
async def test_reauth_flow_update_configuration(hass, aioclient_mock): async def test_reauth_flow_update_configuration(hass, aioclient_mock):
"""Verify reauth flow can update controller configuration.""" """Verify reauth flow can update controller configuration."""
config_entry = await setup_unifi_integration(hass, aioclient_mock) config_entry = await setup_unifi_integration(hass, aioclient_mock)
controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id]
controller.available = False
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
UNIFI_DOMAIN, UNIFI_DOMAIN,
@ -366,7 +378,9 @@ async def test_reauth_flow_update_configuration(hass, aioclient_mock):
aioclient_mock.get( aioclient_mock.get(
"https://1.2.3.4:1234/api/self/sites", "https://1.2.3.4:1234/api/self/sites",
json={ json={
"data": [{"desc": "Site name", "name": "site_id", "role": "admin"}], "data": [
{"desc": "Site name", "name": "site_id", "role": "admin", "_id": "1"}
],
"meta": {"rc": "ok"}, "meta": {"rc": "ok"},
}, },
headers={"content-type": CONTENT_TYPE_JSON}, headers={"content-type": CONTENT_TYPE_JSON},

View File

@ -71,7 +71,7 @@ ENTRY_OPTIONS = {}
CONFIGURATION = [] CONFIGURATION = []
SITE = [{"desc": "Site name", "name": "site_id", "role": "admin"}] SITE = [{"desc": "Site name", "name": "site_id", "role": "admin", "_id": "1"}]
DESCRIPTION = [{"name": "username", "site_name": "site_id", "site_role": "admin"}] DESCRIPTION = [{"name": "username", "site_name": "site_id", "site_role": "admin"}]
@ -166,6 +166,7 @@ async def setup_unifi_integration(
data=deepcopy(config), data=deepcopy(config),
options=deepcopy(options), options=deepcopy(options),
entry_id=1, entry_id=1,
unique_id="1",
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)

View File

@ -45,6 +45,7 @@ async def test_controller_no_mac(hass):
"verify_ssl": True, "verify_ssl": True,
}, },
}, },
unique_id="1",
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
mock_registry = Mock() mock_registry = Mock()