mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 10:17:51 +00:00
Avoid probing ESPHome devices when we do not have the encryption key (#95820)
This commit is contained in:
parent
e8397063d3
commit
995fb993e6
@ -55,6 +55,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
self._host: str | None = None
|
self._host: str | None = None
|
||||||
self._port: int | None = None
|
self._port: int | None = None
|
||||||
self._password: str | None = None
|
self._password: str | None = None
|
||||||
|
self._noise_required: bool | None = None
|
||||||
self._noise_psk: str | None = None
|
self._noise_psk: str | None = None
|
||||||
self._device_info: DeviceInfo | None = None
|
self._device_info: DeviceInfo | None = None
|
||||||
self._reauth_entry: ConfigEntry | None = None
|
self._reauth_entry: ConfigEntry | None = None
|
||||||
@ -151,33 +152,45 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
self.context["title_placeholders"] = {"name": self._name}
|
self.context["title_placeholders"] = {"name": self._name}
|
||||||
|
|
||||||
async def _async_try_fetch_device_info(self) -> FlowResult:
|
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 not self._device_name and not self._noise_psk:
|
||||||
# If device name is not set we can send a zero 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
|
# to get the device name which will allow us to populate
|
||||||
# the device name and hopefully get the encryption key
|
# the device name and hopefully get the encryption key
|
||||||
# from the dashboard.
|
# from the dashboard.
|
||||||
self._noise_psk = ZERO_NOISE_PSK
|
self._noise_psk = ZERO_NOISE_PSK
|
||||||
error = await self.fetch_device_info()
|
response = await self.fetch_device_info()
|
||||||
self._noise_psk = None
|
self._noise_psk = None
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self._device_name
|
self._device_name
|
||||||
and await self._retrieve_encryption_key_from_dashboard()
|
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 the fetched key is invalid, unset it again.
|
||||||
if error == ERROR_INVALID_ENCRYPTION_KEY:
|
if response == ERROR_INVALID_ENCRYPTION_KEY:
|
||||||
self._noise_psk = None
|
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()
|
return await self.async_step_encryption_key()
|
||||||
if error is not None:
|
if response is not None:
|
||||||
return await self._async_step_user_base(error=error)
|
return await self._async_step_user_base(error=response)
|
||||||
return await self._async_authenticate_or_add()
|
return await self._async_authenticate_or_add()
|
||||||
|
|
||||||
async def _async_authenticate_or_add(self) -> FlowResult:
|
async def _async_authenticate_or_add(self) -> FlowResult:
|
||||||
@ -220,6 +233,7 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
self._device_name = device_name
|
self._device_name = device_name
|
||||||
self._host = discovery_info.host
|
self._host = discovery_info.host
|
||||||
self._port = discovery_info.port
|
self._port = discovery_info.port
|
||||||
|
self._noise_required = bool(discovery_info.properties.get("api_encryption"))
|
||||||
|
|
||||||
# Check if already configured
|
# Check if already configured
|
||||||
await self.async_set_unique_id(mac_address)
|
await self.async_set_unique_id(mac_address)
|
||||||
|
@ -1233,6 +1233,72 @@ async def test_zeroconf_encryption_key_via_dashboard(
|
|||||||
assert mock_client.noise_psk == VALID_NOISE_PSK
|
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(
|
async def test_zeroconf_no_encryption_key_via_dashboard(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_client,
|
mock_client,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user