diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index a45077c79c4..cb13cd75944 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -71,6 +71,7 @@ _CLOUD_ERRORS: dict[type[Exception], tuple[HTTPStatus, str]] = { @callback def async_setup(hass: HomeAssistant) -> None: """Initialize the HTTP API.""" + websocket_api.async_register_command(hass, websocket_cloud_remove_data) websocket_api.async_register_command(hass, websocket_cloud_status) websocket_api.async_register_command(hass, websocket_subscription) websocket_api.async_register_command(hass, websocket_update_prefs) @@ -329,6 +330,33 @@ class CloudForgotPasswordView(HomeAssistantView): return self.json_message("ok") +@websocket_api.require_admin +@websocket_api.websocket_command({vol.Required("type"): "cloud/remove_data"}) +@websocket_api.async_response +async def websocket_cloud_remove_data( + hass: HomeAssistant, + connection: websocket_api.ActiveConnection, + msg: dict[str, Any], +) -> None: + """Handle request for account info. + + Async friendly. + """ + cloud: Cloud[CloudClient] = hass.data[DOMAIN] + if cloud.is_logged_in: + connection.send_message( + websocket_api.error_message( + msg["id"], "logged_in", "Can't remove data when logged in." + ) + ) + return + + await cloud.remove_data() + await cloud.client.prefs.async_erase_config() + + connection.send_message(websocket_api.result_message(msg["id"])) + + @websocket_api.websocket_command({vol.Required("type"): "cloud/status"}) @websocket_api.async_response async def websocket_cloud_status( diff --git a/homeassistant/components/cloud/prefs.py b/homeassistant/components/cloud/prefs.py index 7c27aa0f130..0a0989ed4aa 100644 --- a/homeassistant/components/cloud/prefs.py +++ b/homeassistant/components/cloud/prefs.py @@ -204,6 +204,10 @@ class CloudPreferences: return True + async def async_erase_config(self) -> None: + """Erase the configuration.""" + await self._save_prefs(self._empty_config("")) + def as_dict(self) -> dict[str, Any]: """Return dictionary version.""" return { diff --git a/tests/components/cloud/test_http_api.py b/tests/components/cloud/test_http_api.py index 0ff9fd79c75..f7b88fab6c3 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -697,6 +697,45 @@ async def test_resend_confirm_view_unknown_error( assert req.status == HTTPStatus.BAD_GATEWAY +async def test_websocket_remove_data( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + cloud: MagicMock, + setup_cloud: None, +) -> None: + """Test removing cloud data.""" + cloud.id_token = None + client = await hass_ws_client(hass) + + with patch.object(cloud.client.prefs, "async_erase_config") as mock_erase_config: + await client.send_json_auto_id({"type": "cloud/remove_data"}) + response = await client.receive_json() + + assert response["success"] + cloud.remove_data.assert_awaited_once_with() + mock_erase_config.assert_awaited_once_with() + + +async def test_websocket_remove_data_logged_in( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + cloud: MagicMock, + setup_cloud: None, +) -> None: + """Test removing cloud data.""" + cloud.iot.state = STATE_CONNECTED + client = await hass_ws_client(hass) + + await client.send_json_auto_id({"type": "cloud/remove_data"}) + response = await client.receive_json() + + assert not response["success"] + assert response["error"] == { + "code": "logged_in", + "message": "Can't remove data when logged in.", + } + + async def test_websocket_status( hass: HomeAssistant, hass_ws_client: WebSocketGenerator, diff --git a/tests/components/cloud/test_prefs.py b/tests/components/cloud/test_prefs.py index e7d7a174135..86e86a71583 100644 --- a/tests/components/cloud/test_prefs.py +++ b/tests/components/cloud/test_prefs.py @@ -1,7 +1,7 @@ """Test Cloud preferences.""" from typing import Any -from unittest.mock import patch +from unittest.mock import ANY, patch import pytest @@ -26,8 +26,34 @@ async def test_set_username(hass: HomeAssistant) -> None: assert prefs.google_enabled +async def test_erase_config(hass: HomeAssistant) -> None: + """Test erasing config.""" + prefs = CloudPreferences(hass) + await prefs.async_initialize() + assert prefs._prefs == { + **prefs._empty_config(""), + "google_local_webhook_id": ANY, + "instance_id": ANY, + } + + await prefs.async_update(google_enabled=False) + assert prefs._prefs == { + **prefs._empty_config(""), + "google_enabled": False, + "google_local_webhook_id": ANY, + "instance_id": ANY, + } + + await prefs.async_erase_config() + assert prefs._prefs == { + **prefs._empty_config(""), + "google_local_webhook_id": ANY, + "instance_id": ANY, + } + + async def test_set_username_migration(hass: HomeAssistant) -> None: - """Test we not clear config if we had no username.""" + """Test we do not clear config if we had no username.""" prefs = CloudPreferences(hass) with patch.object(prefs, "_empty_config", return_value=prefs._empty_config(None)):