Avoid probing ESPHome devices when we do not have the encryption key (#95820)

pull/96227/head
J. Nick Koston 2023-07-09 12:57:04 -10:00 committed by GitHub
parent e8397063d3
commit 995fb993e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 9 deletions

View File

@ -55,6 +55,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
self._host: str | None = None
self._port: int | None = None
self._password: str | None = None
self._noise_required: bool | None = None
self._noise_psk: str | None = None
self._device_info: DeviceInfo | None = None
self._reauth_entry: ConfigEntry | None = None
@ -151,33 +152,45 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
self.context["title_placeholders"] = {"name": self._name}
async def _async_try_fetch_device_info(self) -> FlowResult:
error = await self.fetch_device_info()
"""Try to fetch device info and return any errors."""
response: str | None
if self._noise_required:
# If we already know we need encryption, don't try to fetch device info
# without encryption.
response = ERROR_REQUIRES_ENCRYPTION_KEY
else:
# After 2024.08, stop trying to fetch device info without encryption
# so we can avoid probe requests to check for password. At this point
# most devices should announce encryption support and password is
# deprecated and can be discovered by trying to connect only after they
# interact with the flow since it is expected to be a rare case.
response = await self.fetch_device_info()
if error == ERROR_REQUIRES_ENCRYPTION_KEY:
if response == ERROR_REQUIRES_ENCRYPTION_KEY:
if not self._device_name and not self._noise_psk:
# If device name is not set we can send a zero noise psk
# to get the device name which will allow us to populate
# the device name and hopefully get the encryption key
# from the dashboard.
self._noise_psk = ZERO_NOISE_PSK
error = await self.fetch_device_info()
response = await self.fetch_device_info()
self._noise_psk = None
if (
self._device_name
and await self._retrieve_encryption_key_from_dashboard()
):
error = await self.fetch_device_info()
response = await self.fetch_device_info()
# If the fetched key is invalid, unset it again.
if error == ERROR_INVALID_ENCRYPTION_KEY:
if response == ERROR_INVALID_ENCRYPTION_KEY:
self._noise_psk = None
error = ERROR_REQUIRES_ENCRYPTION_KEY
response = ERROR_REQUIRES_ENCRYPTION_KEY
if error == ERROR_REQUIRES_ENCRYPTION_KEY:
if response == ERROR_REQUIRES_ENCRYPTION_KEY:
return await self.async_step_encryption_key()
if error is not None:
return await self._async_step_user_base(error=error)
if response is not None:
return await self._async_step_user_base(error=response)
return await self._async_authenticate_or_add()
async def _async_authenticate_or_add(self) -> FlowResult:
@ -220,6 +233,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
self._device_name = device_name
self._host = discovery_info.host
self._port = discovery_info.port
self._noise_required = bool(discovery_info.properties.get("api_encryption"))
# Check if already configured
await self.async_set_unique_id(mac_address)

View File

@ -1233,6 +1233,72 @@ async def test_zeroconf_encryption_key_via_dashboard(
assert mock_client.noise_psk == VALID_NOISE_PSK
async def test_zeroconf_encryption_key_via_dashboard_with_api_encryption_prop(
hass: HomeAssistant,
mock_client,
mock_zeroconf: None,
mock_dashboard,
mock_setup_entry: None,
) -> None:
"""Test encryption key retrieved from dashboard with api_encryption property set."""
service_info = zeroconf.ZeroconfServiceInfo(
host="192.168.43.183",
addresses=["192.168.43.183"],
hostname="test8266.local.",
name="mock_name",
port=6053,
properties={
"mac": "1122334455aa",
"api_encryption": "any",
},
type="mock_type",
)
flow = await hass.config_entries.flow.async_init(
"esphome", context={"source": config_entries.SOURCE_ZEROCONF}, data=service_info
)
assert flow["type"] == FlowResultType.FORM
assert flow["step_id"] == "discovery_confirm"
mock_dashboard["configured"].append(
{
"name": "test8266",
"configuration": "test8266.yaml",
}
)
await dashboard.async_get_dashboard(hass).async_refresh()
mock_client.device_info.side_effect = [
DeviceInfo(
uses_password=False,
name="test8266",
mac_address="11:22:33:44:55:AA",
),
]
with patch(
"homeassistant.components.esphome.dashboard.ESPHomeDashboardAPI.get_encryption_key",
return_value=VALID_NOISE_PSK,
) as mock_get_encryption_key:
result = await hass.config_entries.flow.async_configure(
flow["flow_id"], user_input={}
)
assert len(mock_get_encryption_key.mock_calls) == 1
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == "test8266"
assert result["data"][CONF_HOST] == "192.168.43.183"
assert result["data"][CONF_PORT] == 6053
assert result["data"][CONF_NOISE_PSK] == VALID_NOISE_PSK
assert result["result"]
assert result["result"].unique_id == "11:22:33:44:55:aa"
assert mock_client.noise_psk == VALID_NOISE_PSK
async def test_zeroconf_no_encryption_key_via_dashboard(
hass: HomeAssistant,
mock_client,