mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Improve Awair config flow (#76838)
This commit is contained in:
parent
3e1c9f1ac7
commit
c7d46bc719
@ -4,14 +4,15 @@ from __future__ import annotations
|
|||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aiohttp.client_exceptions import ClientConnectorError
|
from aiohttp.client_exceptions import ClientError
|
||||||
from python_awair import Awair, AwairLocal, AwairLocalDevice
|
from python_awair import Awair, AwairLocal, AwairLocalDevice
|
||||||
from python_awair.exceptions import AuthError, AwairError
|
from python_awair.exceptions import AuthError, AwairError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.components import zeroconf
|
||||||
from homeassistant.config_entries import ConfigFlow
|
from homeassistant.config_entries import SOURCE_ZEROCONF, ConfigFlow
|
||||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST
|
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_DEVICE, CONF_HOST
|
||||||
|
from homeassistant.core import callback
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
@ -40,10 +41,11 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
self._abort_if_unique_id_configured(error="already_configured_device")
|
self._abort_if_unique_id_configured(error="already_configured_device")
|
||||||
self.context.update(
|
self.context.update(
|
||||||
{
|
{
|
||||||
|
"host": host,
|
||||||
"title_placeholders": {
|
"title_placeholders": {
|
||||||
"model": self._device.model,
|
"model": self._device.model,
|
||||||
"device_id": self._device.device_id,
|
"device_id": self._device.device_id,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -109,31 +111,76 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_local(self, user_input: Mapping[str, Any]) -> FlowResult:
|
@callback
|
||||||
|
def _get_discovered_entries(self) -> dict[str, str]:
|
||||||
|
"""Get discovered entries."""
|
||||||
|
entries: dict[str, str] = {}
|
||||||
|
for flow in self._async_in_progress():
|
||||||
|
if flow["context"]["source"] == SOURCE_ZEROCONF:
|
||||||
|
info = flow["context"]["title_placeholders"]
|
||||||
|
entries[
|
||||||
|
flow["context"]["host"]
|
||||||
|
] = f"{info['model']} ({info['device_id']})"
|
||||||
|
return entries
|
||||||
|
|
||||||
|
async def async_step_local(
|
||||||
|
self, user_input: Mapping[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Show how to enable local API."""
|
||||||
|
if user_input is not None:
|
||||||
|
return await self.async_step_local_pick()
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="local",
|
||||||
|
description_placeholders={
|
||||||
|
"url": "https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Element-Local-API-Feature#h_01F40FBBW5323GBPV7D6XMG4J8"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_local_pick(
|
||||||
|
self, user_input: Mapping[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Handle collecting and verifying Awair Local API hosts."""
|
"""Handle collecting and verifying Awair Local API hosts."""
|
||||||
|
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
if user_input is not None:
|
# User input is either:
|
||||||
|
# 1. None if first time on this step
|
||||||
|
# 2. {device: manual} if picked manual entry option
|
||||||
|
# 3. {device: <host>} if picked a device
|
||||||
|
# 4. {host: <host>} if manually entered a host
|
||||||
|
#
|
||||||
|
# Option 1 and 2 will show the form again.
|
||||||
|
if user_input and user_input.get(CONF_DEVICE) != "manual":
|
||||||
|
if CONF_DEVICE in user_input:
|
||||||
|
user_input = {CONF_HOST: user_input[CONF_DEVICE]}
|
||||||
|
|
||||||
self._device, error = await self._check_local_connection(
|
self._device, error = await self._check_local_connection(
|
||||||
user_input[CONF_HOST]
|
user_input.get(CONF_DEVICE) or user_input[CONF_HOST]
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._device is not None:
|
if self._device is not None:
|
||||||
await self.async_set_unique_id(self._device.mac_address)
|
await self.async_set_unique_id(
|
||||||
self._abort_if_unique_id_configured(error="already_configured_device")
|
self._device.mac_address, raise_on_progress=False
|
||||||
|
)
|
||||||
title = f"{self._device.model} ({self._device.device_id})"
|
title = f"{self._device.model} ({self._device.device_id})"
|
||||||
return self.async_create_entry(title=title, data=user_input)
|
return self.async_create_entry(title=title, data=user_input)
|
||||||
|
|
||||||
if error is not None:
|
if error is not None:
|
||||||
errors = {CONF_HOST: error}
|
errors = {"base": error}
|
||||||
|
|
||||||
|
discovered = self._get_discovered_entries()
|
||||||
|
|
||||||
|
if not discovered or (user_input and user_input.get(CONF_DEVICE) == "manual"):
|
||||||
|
data_schema = vol.Schema({vol.Required(CONF_HOST): str})
|
||||||
|
|
||||||
|
elif discovered:
|
||||||
|
discovered["manual"] = "Manual"
|
||||||
|
data_schema = vol.Schema({vol.Required(CONF_DEVICE): vol.In(discovered)})
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="local",
|
step_id="local_pick",
|
||||||
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
|
data_schema=data_schema,
|
||||||
description_placeholders={
|
|
||||||
"url": "https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Element-Local-API-Feature"
|
|
||||||
},
|
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -177,7 +224,7 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
devices = await awair.devices()
|
devices = await awair.devices()
|
||||||
return (devices[0], None)
|
return (devices[0], None)
|
||||||
|
|
||||||
except ClientConnectorError as err:
|
except ClientError as err:
|
||||||
LOGGER.error("Unable to connect error: %s", err)
|
LOGGER.error("Unable to connect error: %s", err)
|
||||||
return (None, "unreachable")
|
return (None, "unreachable")
|
||||||
|
|
||||||
|
@ -12,5 +12,10 @@
|
|||||||
"type": "_http._tcp.local.",
|
"type": "_http._tcp.local.",
|
||||||
"name": "awair*"
|
"name": "awair*"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"dhcp": [
|
||||||
|
{
|
||||||
|
"macaddress": "70886B1*"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"local": {
|
"local": {
|
||||||
|
"description": "Follow [these instructions]({url}) on how to enable the Awair Local API.\n\nClick submit when done."
|
||||||
|
},
|
||||||
|
"local_pick": {
|
||||||
"data": {
|
"data": {
|
||||||
|
"device": "[%key:common::config_flow::data::device%]",
|
||||||
"host": "[%key:common::config_flow::data::ip%]"
|
"host": "[%key:common::config_flow::data::ip%]"
|
||||||
},
|
}
|
||||||
"description": "Awair Local API must be enabled following these steps: {url}"
|
|
||||||
},
|
},
|
||||||
"reauth_confirm": {
|
"reauth_confirm": {
|
||||||
"description": "Please re-enter your Awair developer access token.",
|
"description": "Please re-enter your Awair developer access token.",
|
||||||
|
@ -11,6 +11,7 @@ DHCP: list[dict[str, str | bool]] = [
|
|||||||
{'domain': 'august', 'hostname': 'connect', 'macaddress': 'B8B7F1*'},
|
{'domain': 'august', 'hostname': 'connect', 'macaddress': 'B8B7F1*'},
|
||||||
{'domain': 'august', 'hostname': 'connect', 'macaddress': '2C9FFB*'},
|
{'domain': 'august', 'hostname': 'connect', 'macaddress': '2C9FFB*'},
|
||||||
{'domain': 'august', 'hostname': 'august*', 'macaddress': 'E076D0*'},
|
{'domain': 'august', 'hostname': 'august*', 'macaddress': 'E076D0*'},
|
||||||
|
{'domain': 'awair', 'macaddress': '70886B1*'},
|
||||||
{'domain': 'axis', 'registered_devices': True},
|
{'domain': 'axis', 'registered_devices': True},
|
||||||
{'domain': 'axis', 'hostname': 'axis-00408c*', 'macaddress': '00408C*'},
|
{'domain': 'axis', 'hostname': 'axis-00408c*', 'macaddress': '00408C*'},
|
||||||
{'domain': 'axis', 'hostname': 'axis-accc8e*', 'macaddress': 'ACCC8E*'},
|
{'domain': 'axis', 'hostname': 'axis-accc8e*', 'macaddress': 'ACCC8E*'},
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
"confirm_setup": "Do you want to start set up?"
|
"confirm_setup": "Do you want to start set up?"
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
|
"device": "Device",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
|
@ -240,6 +240,12 @@ async def test_create_local_entry(hass: HomeAssistant, local_devices):
|
|||||||
{"next_step_id": "local"},
|
{"next_step_id": "local"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# We're being shown the local instructions
|
||||||
|
form_step = await hass.config_entries.flow.async_configure(
|
||||||
|
form_step["flow_id"],
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
form_step["flow_id"],
|
form_step["flow_id"],
|
||||||
LOCAL_CONFIG,
|
LOCAL_CONFIG,
|
||||||
@ -251,6 +257,48 @@ async def test_create_local_entry(hass: HomeAssistant, local_devices):
|
|||||||
assert result["result"].unique_id == LOCAL_UNIQUE_ID
|
assert result["result"].unique_id == LOCAL_UNIQUE_ID
|
||||||
|
|
||||||
|
|
||||||
|
async def test_create_local_entry_from_discovery(hass: HomeAssistant, local_devices):
|
||||||
|
"""Test local API when device discovered after instructions shown."""
|
||||||
|
|
||||||
|
menu_step = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}, data=LOCAL_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
form_step = await hass.config_entries.flow.async_configure(
|
||||||
|
menu_step["flow_id"],
|
||||||
|
{"next_step_id": "local"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create discovered entry in progress
|
||||||
|
with patch("python_awair.AwairClient.query", side_effect=[local_devices]):
|
||||||
|
await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
data=Mock(host=LOCAL_CONFIG[CONF_HOST]),
|
||||||
|
context={"source": SOURCE_ZEROCONF},
|
||||||
|
)
|
||||||
|
|
||||||
|
# We're being shown the local instructions
|
||||||
|
form_step = await hass.config_entries.flow.async_configure(
|
||||||
|
form_step["flow_id"],
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch("python_awair.AwairClient.query", side_effect=[local_devices]), patch(
|
||||||
|
"homeassistant.components.awair.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
form_step["flow_id"],
|
||||||
|
{"device": LOCAL_CONFIG[CONF_HOST]},
|
||||||
|
)
|
||||||
|
|
||||||
|
print(result)
|
||||||
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == "Awair Element (24947)"
|
||||||
|
assert result["data"][CONF_HOST] == LOCAL_CONFIG[CONF_HOST]
|
||||||
|
assert result["result"].unique_id == LOCAL_UNIQUE_ID
|
||||||
|
|
||||||
|
|
||||||
async def test_create_local_entry_awair_error(hass: HomeAssistant):
|
async def test_create_local_entry_awair_error(hass: HomeAssistant):
|
||||||
"""Test overall flow when using local API and device is returns error."""
|
"""Test overall flow when using local API and device is returns error."""
|
||||||
|
|
||||||
@ -267,6 +315,12 @@ async def test_create_local_entry_awair_error(hass: HomeAssistant):
|
|||||||
{"next_step_id": "local"},
|
{"next_step_id": "local"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# We're being shown the local instructions
|
||||||
|
form_step = await hass.config_entries.flow.async_configure(
|
||||||
|
form_step["flow_id"],
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
form_step["flow_id"],
|
form_step["flow_id"],
|
||||||
LOCAL_CONFIG,
|
LOCAL_CONFIG,
|
||||||
@ -274,7 +328,7 @@ async def test_create_local_entry_awair_error(hass: HomeAssistant):
|
|||||||
|
|
||||||
# User is returned to form to try again
|
# User is returned to form to try again
|
||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["step_id"] == "local"
|
assert result["step_id"] == "local_pick"
|
||||||
|
|
||||||
|
|
||||||
async def test_create_zeroconf_entry(hass: HomeAssistant, local_devices):
|
async def test_create_zeroconf_entry(hass: HomeAssistant, local_devices):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user