Avoid setting up ESPHome dashboard if its been uninstalled (#142904)

* Avoid setting up ESPHome dashboard if its been uninstalled

* tweaks

* coverage

* coverage

* fix
This commit is contained in:
J. Nick Koston 2025-04-13 21:49:21 -10:00 committed by GitHub
parent bfc3080292
commit 8bcc4f4c82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 61 additions and 7 deletions

View File

@ -10,6 +10,7 @@ from homeassistant.config_entries import SOURCE_REAUTH
from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.singleton import singleton from homeassistant.helpers.singleton import singleton
from homeassistant.helpers.storage import Store from homeassistant.helpers.storage import Store
from homeassistant.util.hass_dict import HassKey from homeassistant.util.hass_dict import HassKey
@ -60,7 +61,22 @@ class ESPHomeDashboardManager:
async def async_setup(self) -> None: async def async_setup(self) -> None:
"""Restore the dashboard from storage.""" """Restore the dashboard from storage."""
self._data = await self._store.async_load() self._data = await self._store.async_load()
if (data := self._data) and (info := data.get("info")): if not (data := self._data) or not (info := data.get("info")):
return
if is_hassio(self._hass):
from homeassistant.components.hassio import ( # pylint: disable=import-outside-toplevel
get_addons_info,
)
if (addons := get_addons_info(self._hass)) is not None and info[
"addon_slug"
] not in addons:
# The addon is not installed anymore, but it make come back
# so we don't want to remove the dashboard, but for now
# we don't want to use it.
_LOGGER.debug("Addon %s is no longer installed", info["addon_slug"])
return
await self.async_set_dashboard_info( await self.async_set_dashboard_info(
info["addon_slug"], info["host"], info["port"] info["addon_slug"], info["host"], info["port"]
) )

View File

@ -1,7 +1,7 @@
{ {
"domain": "esphome", "domain": "esphome",
"name": "ESPHome", "name": "ESPHome",
"after_dependencies": ["zeroconf", "tag"], "after_dependencies": ["hassio", "zeroconf", "tag"],
"codeowners": ["@OttoWinter", "@jesserockz", "@kbx81", "@bdraco"], "codeowners": ["@OttoWinter", "@jesserockz", "@kbx81", "@bdraco"],
"config_flow": true, "config_flow": true,
"dependencies": ["assist_pipeline", "bluetooth", "intent", "ffmpeg", "http"], "dependencies": ["assist_pipeline", "bluetooth", "intent", "ffmpeg", "http"],

View File

@ -4,6 +4,7 @@ from typing import Any
from unittest.mock import patch from unittest.mock import patch
from aioesphomeapi import DeviceInfo, InvalidAuthAPIError from aioesphomeapi import DeviceInfo, InvalidAuthAPIError
import pytest
from homeassistant.components.esphome import CONF_NOISE_PSK, coordinator, dashboard from homeassistant.components.esphome import CONF_NOISE_PSK, coordinator, dashboard
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
@ -63,15 +64,52 @@ async def test_restore_dashboard_storage_end_to_end(
"key": dashboard.STORAGE_KEY, "key": dashboard.STORAGE_KEY,
"data": {"info": {"addon_slug": "test-slug", "host": "new-host", "port": 6052}}, "data": {"info": {"addon_slug": "test-slug", "host": "new-host", "port": 6052}},
} }
with patch( with (
patch(
"homeassistant.components.esphome.dashboard.is_hassio", return_value=False
),
patch(
"homeassistant.components.esphome.coordinator.ESPHomeDashboardAPI" "homeassistant.components.esphome.coordinator.ESPHomeDashboardAPI"
) as mock_dashboard_api: ) as mock_dashboard_api,
):
await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.LOADED assert mock_config_entry.state is ConfigEntryState.LOADED
assert mock_dashboard_api.mock_calls[0][1][0] == "http://new-host:6052" assert mock_dashboard_api.mock_calls[0][1][0] == "http://new-host:6052"
async def test_restore_dashboard_storage_skipped_if_addon_uninstalled(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
hass_storage: dict[str, Any],
caplog: pytest.LogCaptureFixture,
) -> None:
"""Restore dashboard restore is skipped if the addon is uninstalled."""
hass_storage[dashboard.STORAGE_KEY] = {
"version": dashboard.STORAGE_VERSION,
"minor_version": dashboard.STORAGE_VERSION,
"key": dashboard.STORAGE_KEY,
"data": {"info": {"addon_slug": "test-slug", "host": "new-host", "port": 6052}},
}
with (
patch(
"homeassistant.components.esphome.coordinator.ESPHomeDashboardAPI"
) as mock_dashboard_api,
patch(
"homeassistant.components.esphome.dashboard.is_hassio", return_value=True
),
patch(
"homeassistant.components.hassio.get_addons_info",
return_value={},
),
):
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.LOADED
assert "test-slug is no longer installed" in caplog.text
assert not mock_dashboard_api.called
async def test_setup_dashboard_fails( async def test_setup_dashboard_fails(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,