diff --git a/homeassistant/components/velux/__init__.py b/homeassistant/components/velux/__init__.py index 594affd95392..193f9e78987e 100644 --- a/homeassistant/components/velux/__init__.py +++ b/homeassistant/components/velux/__init__.py @@ -95,4 +95,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: VeluxConfigEntry) -> boo async def async_unload_entry(hass: HomeAssistant, entry: VeluxConfigEntry) -> bool: """Unload a config entry.""" - return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + # Disconnect from gateway only after platforms are successfully unloaded. + # Disconnecting will reboot the gateway in the pyvlx library, which is needed to allow new + # connections to be made later. + await entry.runtime_data.disconnect() + return unload_ok diff --git a/homeassistant/components/velux/quality_scale.yaml b/homeassistant/components/velux/quality_scale.yaml index 5895a83909a1..2e0d16707868 100644 --- a/homeassistant/components/velux/quality_scale.yaml +++ b/homeassistant/components/velux/quality_scale.yaml @@ -25,7 +25,7 @@ rules: # Silver action-exceptions: todo - config-entry-unloading: todo + config-entry-unloading: done docs-configuration-parameters: todo docs-installation-parameters: todo entity-unavailable: todo diff --git a/tests/components/velux/test_init.py b/tests/components/velux/test_init.py index bf4e02b47e89..fc17df0a8030 100644 --- a/tests/components/velux/test_init.py +++ b/tests/components/velux/test_init.py @@ -2,16 +2,22 @@ These tests verify that setup retries (ConfigEntryNotReady) are triggered when scene or node loading fails. + +They also verify that unloading the integration properly disconnects. """ from __future__ import annotations +from unittest.mock import patch + +import pytest from pyvlx.exception import PyVLXException from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from tests.common import AsyncMock, ConfigEntry +from tests.common import AsyncMock, ConfigEntry, MockConfigEntry async def test_setup_retry_on_nodes_failure( @@ -53,3 +59,44 @@ async def test_setup_retry_on_oserror_during_scenes( assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY mock_pyvlx.load_scenes.assert_awaited_once() mock_pyvlx.load_nodes.assert_not_called() + + +@pytest.fixture +def platform() -> Platform: + """Fixture to specify platform to test.""" + return Platform.COVER + + +@pytest.mark.usefixtures("setup_integration") +async def test_unload_calls_disconnect( + hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_pyvlx +) -> None: + """Test that unloading the config entry disconnects from the gateway.""" + + # Unload the entry + await hass.config_entries.async_unload(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Verify disconnect was called + mock_pyvlx.disconnect.assert_awaited_once() + + +@pytest.mark.usefixtures("setup_integration") +async def test_unload_does_not_disconnect_if_platform_unload_fails( + hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_pyvlx +) -> None: + """Test that disconnect is not called if platform unload fails.""" + + # Mock platform unload to fail + with patch( + "homeassistant.config_entries.ConfigEntries.async_unload_platforms", + return_value=False, + ): + result = await hass.config_entries.async_unload(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Verify unload failed + assert result is False + + # Verify disconnect was NOT called since platform unload failed + mock_pyvlx.disconnect.assert_not_awaited()