diff --git a/homeassistant/components/esphome/dashboard.py b/homeassistant/components/esphome/dashboard.py index 3ce07d683b9..052d6161cf9 100644 --- a/homeassistant/components/esphome/dashboard.py +++ b/homeassistant/components/esphome/dashboard.py @@ -8,7 +8,7 @@ import logging import aiohttp from esphome_dashboard_api import ConfiguredDevice, ESPHomeDashboardAPI -from homeassistant.config_entries import ConfigEntryState +from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -52,8 +52,14 @@ async def async_set_dashboard_info( for entry in hass.config_entries.async_entries(DOMAIN) if entry.state == ConfigEntryState.LOADED ] - if reloads: - await asyncio.gather(*reloads) + # Re-auth flows will check the dashboard for encryption key when the form is requested + reauths = [ + hass.config_entries.flow.async_configure(flow["flow_id"]) + for flow in hass.config_entries.flow.async_progress() + if flow["handler"] == DOMAIN and flow["context"]["source"] == SOURCE_REAUTH + ] + if reloads or reauths: + await asyncio.gather(*reloads, *reauths) class ESPHomeDashboard(DataUpdateCoordinator[dict[str, ConfiguredDevice]]): diff --git a/tests/components/esphome/__init__.py b/tests/components/esphome/__init__.py index 764a06f3bb9..a44db03f841 100644 --- a/tests/components/esphome/__init__.py +++ b/tests/components/esphome/__init__.py @@ -3,3 +3,4 @@ DASHBOARD_SLUG = "mock-slug" DASHBOARD_HOST = "mock-host" DASHBOARD_PORT = 1234 +VALID_NOISE_PSK = "bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU=" diff --git a/tests/components/esphome/conftest.py b/tests/components/esphome/conftest.py index 6febe15389a..f53e513e6bb 100644 --- a/tests/components/esphome/conftest.py +++ b/tests/components/esphome/conftest.py @@ -7,7 +7,12 @@ from aioesphomeapi import APIClient, DeviceInfo import pytest from zeroconf import Zeroconf -from homeassistant.components.esphome import CONF_NOISE_PSK, DOMAIN, dashboard +from homeassistant.components.esphome import ( + CONF_DEVICE_NAME, + CONF_NOISE_PSK, + DOMAIN, + dashboard, +) from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT from homeassistant.core import HomeAssistant @@ -27,9 +32,9 @@ def esphome_mock_async_zeroconf(mock_async_zeroconf): @pytest.fixture -def mock_config_entry() -> MockConfigEntry: +def mock_config_entry(hass) -> MockConfigEntry: """Return the default mocked config entry.""" - return MockConfigEntry( + config_entry = MockConfigEntry( title="ESPHome Device", domain=DOMAIN, data={ @@ -37,9 +42,12 @@ def mock_config_entry() -> MockConfigEntry: CONF_PORT: 6053, CONF_PASSWORD: "pwd", CONF_NOISE_PSK: "12345678123456781234567812345678", + CONF_DEVICE_NAME: "test", }, unique_id="11:22:33:44:55:aa", ) + config_entry.add_to_hass(hass) + return config_entry @pytest.fixture @@ -59,8 +67,6 @@ async def init_integration( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> MockConfigEntry: """Set up the ESPHome integration for testing.""" - mock_config_entry.add_to_hass(hass) - await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done() diff --git a/tests/components/esphome/test_config_flow.py b/tests/components/esphome/test_config_flow.py index f7326d05b8f..92e2df5ca39 100644 --- a/tests/components/esphome/test_config_flow.py +++ b/tests/components/esphome/test_config_flow.py @@ -24,9 +24,10 @@ from homeassistant.components.hassio import HassioServiceInfo from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT from homeassistant.data_entry_flow import FlowResultType +from . import VALID_NOISE_PSK + from tests.common import MockConfigEntry -VALID_NOISE_PSK = "bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU=" INVALID_NOISE_PSK = "lSYBYEjQI1bVL8s2Vask4YytGMj1f1epNtmoim2yuTM=" diff --git a/tests/components/esphome/test_dashboard.py b/tests/components/esphome/test_dashboard.py index 7a5486d5205..c14bb06d3d8 100644 --- a/tests/components/esphome/test_dashboard.py +++ b/tests/components/esphome/test_dashboard.py @@ -1,8 +1,13 @@ """Test ESPHome dashboard features.""" from unittest.mock import patch -from homeassistant.components.esphome import dashboard -from homeassistant.config_entries import ConfigEntryState +from aioesphomeapi import DeviceInfo + +from homeassistant.components.esphome import CONF_NOISE_PSK, dashboard +from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState +from homeassistant.data_entry_flow import FlowResultType + +from . import VALID_NOISE_PSK async def test_new_info_reload_config_entries(hass, init_integration, mock_dashboard): @@ -20,3 +25,48 @@ async def test_new_info_reload_config_entries(hass, init_integration, mock_dashb await dashboard.async_set_dashboard_info(hass, "test-slug", "test-host", 6052) assert len(mock_setup.mock_calls) == 0 + + +async def test_new_dashboard_fix_reauth( + hass, mock_client, mock_config_entry, mock_dashboard +): + """Test config entries waiting for reauth are triggered.""" + mock_client.device_info.return_value = DeviceInfo(uses_password=False, name="test") + + 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_init( + "esphome", + context={ + "source": SOURCE_REAUTH, + "entry_id": mock_config_entry.entry_id, + "unique_id": mock_config_entry.unique_id, + }, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "reauth_confirm" + assert len(mock_get_encryption_key.mock_calls) == 0 + + mock_dashboard["configured"].append( + { + "name": "test", + "configuration": "test.yaml", + } + ) + + await dashboard.async_get_dashboard(hass).async_refresh() + + with patch( + "homeassistant.components.esphome.dashboard.ESPHomeDashboardAPI.get_encryption_key", + return_value=VALID_NOISE_PSK, + ) as mock_get_encryption_key, patch( + "homeassistant.components.esphome.async_setup_entry", return_value=True + ) as mock_setup: + await dashboard.async_set_dashboard_info(hass, "test-slug", "test-host", 6052) + await hass.async_block_till_done() + + assert len(mock_get_encryption_key.mock_calls) == 1 + assert len(mock_setup.mock_calls) == 1 + assert mock_config_entry.data[CONF_NOISE_PSK] == VALID_NOISE_PSK diff --git a/tests/components/esphome/test_diagnostics.py b/tests/components/esphome/test_diagnostics.py index 959d49c4ee3..6a08a47e6eb 100644 --- a/tests/components/esphome/test_diagnostics.py +++ b/tests/components/esphome/test_diagnostics.py @@ -3,7 +3,7 @@ from aiohttp import ClientSession import pytest -from homeassistant.components.esphome import CONF_NOISE_PSK +from homeassistant.components.esphome import CONF_DEVICE_NAME, CONF_NOISE_PSK from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT from homeassistant.core import HomeAssistant @@ -25,6 +25,7 @@ async def test_diagnostics( assert isinstance(result, dict) assert result["config"]["data"] == { + CONF_DEVICE_NAME: "test", CONF_HOST: "192.168.1.2", CONF_PORT: 6053, CONF_PASSWORD: "**REDACTED**",