mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Reinitialize hassio discovery flow on config entry removal (#127088)
* Reinitialize hassio discovery flow on config entry removal * Address review comments
This commit is contained in:
parent
c9311ea3c9
commit
cee7017d20
@ -16,8 +16,9 @@ from homeassistant.const import ATTR_SERVICE, EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import BaseServiceInfo
|
||||
from homeassistant.helpers import discovery_flow
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import ATTR_ADDON, ATTR_CONFIG, ATTR_DISCOVERY, ATTR_UUID
|
||||
from .const import ATTR_ADDON, ATTR_CONFIG, ATTR_DISCOVERY, ATTR_UUID, DOMAIN
|
||||
from .handler import HassIO, HassioAPIError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -59,6 +60,23 @@ def async_setup_discovery_view(hass: HomeAssistant, hassio: HassIO) -> None:
|
||||
EVENT_HOMEASSISTANT_START, _async_discovery_start_handler
|
||||
)
|
||||
|
||||
async def _handle_config_entry_removed(
|
||||
entry: config_entries.ConfigEntry,
|
||||
) -> None:
|
||||
"""Handle config entry changes."""
|
||||
for disc_key in entry.discovery_keys[DOMAIN]:
|
||||
if disc_key.version != 1 or not isinstance(key := disc_key.key, str):
|
||||
continue
|
||||
uuid = key
|
||||
_LOGGER.debug("Rediscover addon %s", uuid)
|
||||
await hassio_discovery.async_rediscover(uuid)
|
||||
|
||||
async_dispatcher_connect(
|
||||
hass,
|
||||
config_entries.signal_discovered_config_entry_removed(DOMAIN),
|
||||
_handle_config_entry_removed,
|
||||
)
|
||||
|
||||
|
||||
class HassIODiscovery(HomeAssistantView):
|
||||
"""Hass.io view to handle base part."""
|
||||
@ -90,6 +108,15 @@ class HassIODiscovery(HomeAssistantView):
|
||||
await self.async_process_del(data)
|
||||
return web.Response()
|
||||
|
||||
async def async_rediscover(self, uuid: str) -> None:
|
||||
"""Rediscover add-on when config entry is removed."""
|
||||
try:
|
||||
data = await self.hassio.get_discovery_message(uuid)
|
||||
except HassioAPIError as err:
|
||||
_LOGGER.debug("Can't read discovery data: %s", err)
|
||||
else:
|
||||
await self.async_process_new(data)
|
||||
|
||||
async def async_process_new(self, data: dict[str, Any]) -> None:
|
||||
"""Process add discovery entry."""
|
||||
service: str = data[ATTR_SERVICE]
|
||||
@ -114,6 +141,11 @@ class HassIODiscovery(HomeAssistantView):
|
||||
data=HassioServiceInfo(
|
||||
config=config_data, name=addon_info.name, slug=slug, uuid=uuid
|
||||
),
|
||||
discovery_key=discovery_flow.DiscoveryKey(
|
||||
domain=DOMAIN,
|
||||
key=data[ATTR_UUID],
|
||||
version=1,
|
||||
),
|
||||
)
|
||||
|
||||
async def async_process_del(self, data: dict[str, Any]) -> None:
|
||||
|
@ -13,9 +13,16 @@ from homeassistant.components.hassio.handler import HassioAPIError
|
||||
from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STARTED
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.discovery_flow import DiscoveryKey
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockModule, mock_config_flow, mock_integration, mock_platform
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockModule,
|
||||
mock_config_flow,
|
||||
mock_integration,
|
||||
mock_platform,
|
||||
)
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
@ -218,3 +225,154 @@ async def test_hassio_discovery_webhook(
|
||||
uuid="test",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"entry_domain",
|
||||
"entry_discovery_keys",
|
||||
),
|
||||
[
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{"hassio": (DiscoveryKey(domain="hassio", key="test", version=1),)},
|
||||
),
|
||||
# Matching discovery key
|
||||
(
|
||||
"mock-domain",
|
||||
{
|
||||
"hassio": (DiscoveryKey(domain="hassio", key="test", version=1),),
|
||||
"other": (DiscoveryKey(domain="other", key="blah", version=1),),
|
||||
},
|
||||
),
|
||||
# Matching discovery key, other domain
|
||||
# Note: Rediscovery is not currently restricted to the domain of the removed
|
||||
# entry. Such a check can be added if needed.
|
||||
(
|
||||
"comp",
|
||||
{"hassio": (DiscoveryKey(domain="hassio", key="test", version=1),)},
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"entry_source",
|
||||
[
|
||||
config_entries.SOURCE_HASSIO,
|
||||
config_entries.SOURCE_IGNORE,
|
||||
config_entries.SOURCE_USER,
|
||||
],
|
||||
)
|
||||
async def test_hassio_rediscover(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
hassio_client: TestClient,
|
||||
addon_installed: AsyncMock,
|
||||
entry_domain: str,
|
||||
entry_discovery_keys: dict[str, tuple[DiscoveryKey, ...]],
|
||||
entry_source: str,
|
||||
) -> None:
|
||||
"""Test we reinitiate flows when an ignored config entry is removed."""
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=entry_domain,
|
||||
discovery_keys=entry_discovery_keys,
|
||||
unique_id="mock-unique-id",
|
||||
state=config_entries.ConfigEntryState.LOADED,
|
||||
source=entry_source,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/discovery/test",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"service": "mqtt",
|
||||
"uuid": "test",
|
||||
"addon": "mosquitto",
|
||||
"config": {
|
||||
"broker": "mock-broker",
|
||||
"port": 1883,
|
||||
"username": "mock-user",
|
||||
"password": "mock-pass",
|
||||
"protocol": "3.1.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/discovery", json={"result": "ok", "data": {"discovery": []}}
|
||||
)
|
||||
|
||||
expected_context = {
|
||||
"discovery_key": DiscoveryKey(domain="hassio", key="test", version=1),
|
||||
"source": config_entries.SOURCE_HASSIO,
|
||||
}
|
||||
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_init.mock_calls) == 1
|
||||
assert mock_init.mock_calls[0][1][0] == "mqtt"
|
||||
assert mock_init.mock_calls[0][2]["context"] == expected_context
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_async_zeroconf")
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"entry_domain",
|
||||
"entry_discovery_keys",
|
||||
"entry_source",
|
||||
"entry_unique_id",
|
||||
),
|
||||
[
|
||||
# Discovery key from other domain
|
||||
(
|
||||
"mock-domain",
|
||||
{"bluetooth": (DiscoveryKey(domain="bluetooth", key="test", version=1),)},
|
||||
config_entries.SOURCE_IGNORE,
|
||||
"mock-unique-id",
|
||||
),
|
||||
# Discovery key from the future
|
||||
(
|
||||
"mock-domain",
|
||||
{"hassio": (DiscoveryKey(domain="hassio", key="test", version=2),)},
|
||||
config_entries.SOURCE_IGNORE,
|
||||
"mock-unique-id",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_hassio_rediscover_no_match(
|
||||
hass: HomeAssistant,
|
||||
hassio_client: TestClient,
|
||||
entry_domain: str,
|
||||
entry_discovery_keys: dict[str, tuple[DiscoveryKey, ...]],
|
||||
entry_source: str,
|
||||
entry_unique_id: str,
|
||||
) -> None:
|
||||
"""Test we don't reinitiate flows when a non matching config entry is removed."""
|
||||
|
||||
mock_integration(hass, MockModule(entry_domain))
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=entry_domain,
|
||||
discovery_keys=entry_discovery_keys,
|
||||
unique_id=entry_unique_id,
|
||||
state=config_entries.ConfigEntryState.LOADED,
|
||||
source=entry_source,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(mock_init.mock_calls) == 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user