ESPHome handle remove password and no encryption (#86995)

* ESPHome handle remove password and no encryption

* Start reauth for invalid api password

---------

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Paulus Schoutsen 2023-01-30 23:05:48 -05:00 committed by GitHub
parent e706696271
commit d88849fb04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 21 deletions

View File

@ -17,6 +17,7 @@ from aioesphomeapi import (
EntityInfo, EntityInfo,
EntityState, EntityState,
HomeassistantServiceCall, HomeassistantServiceCall,
InvalidAuthAPIError,
InvalidEncryptionKeyAPIError, InvalidEncryptionKeyAPIError,
ReconnectLogic, ReconnectLogic,
RequiresEncryptionAPIError, RequiresEncryptionAPIError,
@ -347,7 +348,14 @@ async def async_setup_entry( # noqa: C901
async def on_connect_error(err: Exception) -> None: async def on_connect_error(err: Exception) -> None:
"""Start reauth flow if appropriate connect error type.""" """Start reauth flow if appropriate connect error type."""
if isinstance(err, (RequiresEncryptionAPIError, InvalidEncryptionKeyAPIError)): if isinstance(
err,
(
RequiresEncryptionAPIError,
InvalidEncryptionKeyAPIError,
InvalidAuthAPIError,
),
):
entry.async_start_reauth(hass) entry.async_start_reauth(hass)
reconnect_logic = ReconnectLogic( reconnect_logic = ReconnectLogic(

View File

@ -92,6 +92,19 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
self._name = entry.title self._name = entry.title
self._device_name = entry.data.get(CONF_DEVICE_NAME) self._device_name = entry.data.get(CONF_DEVICE_NAME)
# Device without encryption allows fetching device info. We can then check
# if the device is no longer using a password. If we did try with a password,
# we know setting password to empty will allow us to authenticate.
error = await self.fetch_device_info()
if (
error is None
and self._password
and self._device_info
and not self._device_info.uses_password
):
self._password = ""
return await self._async_authenticate_or_add()
return await self.async_step_reauth_confirm() return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm( async def async_step_reauth_confirm(

View File

@ -519,23 +519,14 @@ async def test_reauth_fixed_via_dashboard(
assert len(mock_get_encryption_key.mock_calls) == 1 assert len(mock_get_encryption_key.mock_calls) == 1
async def test_reauth_fixed_via_dashboard_remove_password( async def test_reauth_fixed_via_dashboard_add_encryption_remove_password(
hass, mock_client, mock_zeroconf, mock_dashboard hass, mock_client, mock_zeroconf, mock_dashboard, mock_config_entry
): ):
"""Test reauth fixed automatically via dashboard with password removed.""" """Test reauth fixed automatically via dashboard with password removed."""
mock_client.device_info.side_effect = (
entry = MockConfigEntry( InvalidAuthAPIError,
domain=DOMAIN, DeviceInfo(uses_password=False, name="test"),
data={
CONF_HOST: "127.0.0.1",
CONF_PORT: 6053,
CONF_PASSWORD: "hello",
CONF_DEVICE_NAME: "test",
},
) )
entry.add_to_hass(hass)
mock_client.device_info.return_value = DeviceInfo(uses_password=False, name="test")
mock_dashboard["configured"].append( mock_dashboard["configured"].append(
{ {
@ -554,19 +545,37 @@ async def test_reauth_fixed_via_dashboard_remove_password(
"esphome", "esphome",
context={ context={
"source": config_entries.SOURCE_REAUTH, "source": config_entries.SOURCE_REAUTH,
"entry_id": entry.entry_id, "entry_id": mock_config_entry.entry_id,
"unique_id": entry.unique_id, "unique_id": mock_config_entry.unique_id,
}, },
) )
assert result["type"] == FlowResultType.ABORT, result assert result["type"] == FlowResultType.ABORT, result
assert result["reason"] == "reauth_successful" assert result["reason"] == "reauth_successful"
assert entry.data[CONF_NOISE_PSK] == VALID_NOISE_PSK assert mock_config_entry.data[CONF_NOISE_PSK] == VALID_NOISE_PSK
assert entry.data[CONF_PASSWORD] == "" assert mock_config_entry.data[CONF_PASSWORD] == ""
assert len(mock_get_encryption_key.mock_calls) == 1 assert len(mock_get_encryption_key.mock_calls) == 1
async def test_reauth_fixed_via_remove_password(hass, mock_client, mock_config_entry):
"""Test reauth fixed automatically by seeing password removed."""
mock_client.device_info.return_value = DeviceInfo(uses_password=False, name="test")
result = await hass.config_entries.flow.async_init(
"esphome",
context={
"source": config_entries.SOURCE_REAUTH,
"entry_id": mock_config_entry.entry_id,
"unique_id": mock_config_entry.unique_id,
},
)
assert result["type"] == FlowResultType.ABORT, result
assert result["reason"] == "reauth_successful"
assert mock_config_entry.data[CONF_PASSWORD] == ""
async def test_reauth_fixed_via_dashboard_at_confirm( async def test_reauth_fixed_via_dashboard_at_confirm(
hass, mock_client, mock_zeroconf, mock_dashboard hass, mock_client, mock_zeroconf, mock_dashboard
): ):

View File

@ -1,7 +1,7 @@
"""Test ESPHome dashboard features.""" """Test ESPHome dashboard features."""
from unittest.mock import patch from unittest.mock import patch
from aioesphomeapi import DeviceInfo from aioesphomeapi import DeviceInfo, InvalidAuthAPIError
from homeassistant.components.esphome import CONF_NOISE_PSK, dashboard from homeassistant.components.esphome import CONF_NOISE_PSK, dashboard
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
@ -31,7 +31,10 @@ async def test_new_dashboard_fix_reauth(
hass, mock_client, mock_config_entry, mock_dashboard hass, mock_client, mock_config_entry, mock_dashboard
): ):
"""Test config entries waiting for reauth are triggered.""" """Test config entries waiting for reauth are triggered."""
mock_client.device_info.return_value = DeviceInfo(uses_password=False, name="test") mock_client.device_info.side_effect = (
InvalidAuthAPIError,
DeviceInfo(uses_password=False, name="test"),
)
with patch( with patch(
"homeassistant.components.esphome.dashboard.ESPHomeDashboardAPI.get_encryption_key", "homeassistant.components.esphome.dashboard.ESPHomeDashboardAPI.get_encryption_key",