diff --git a/homeassistant/components/netatmo/diagnostics.py b/homeassistant/components/netatmo/diagnostics.py new file mode 100644 index 00000000000..5d9754ea69c --- /dev/null +++ b/homeassistant/components/netatmo/diagnostics.py @@ -0,0 +1,44 @@ +"""Diagnostics support for Netatmo.""" +from __future__ import annotations + +from homeassistant.components.diagnostics import REDACTED +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DATA_HANDLER, DOMAIN +from .data_handler import NetatmoDataHandler + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry +) -> dict: + """Return diagnostics for a config entry.""" + data_handler: NetatmoDataHandler = hass.data[DOMAIN][config_entry.entry_id][ + DATA_HANDLER + ] + + diagnostics_data = { + "info": { + **config_entry.as_dict(), + "webhook_registered": data_handler.webhook, + }, + "data": data_handler.data, + } + + if "token" in diagnostics_data["info"]["data"]: + diagnostics_data["info"]["data"]["token"]["access_token"] = REDACTED + diagnostics_data["info"]["data"]["token"]["refresh_token"] = REDACTED + diagnostics_data["info"]["data"]["token"]["restricted_access_token"] = REDACTED + diagnostics_data["info"]["data"]["token"]["restricted_refresh_token"] = REDACTED + + if "webhook_id" in diagnostics_data["info"]["data"]: + diagnostics_data["info"]["data"]["webhook_id"] = REDACTED + + if "weather_areas" in diagnostics_data["info"].get("options", {}): + for area in diagnostics_data["info"]["options"]["weather_areas"]: + for attr in ("lat_ne", "lat_sw", "lon_ne", "lon_sw"): + diagnostics_data["info"]["options"]["weather_areas"][area][ + attr + ] = REDACTED + + return diagnostics_data diff --git a/tests/components/netatmo/test_diagnostics.py b/tests/components/netatmo/test_diagnostics.py new file mode 100644 index 00000000000..62e99cba8b2 --- /dev/null +++ b/tests/components/netatmo/test_diagnostics.py @@ -0,0 +1,96 @@ +"""Test the Netatmo diagnostics.""" +from unittest.mock import AsyncMock, patch + +from homeassistant.setup import async_setup_component + +from .common import fake_post_request + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_entry_diagnostics(hass, hass_client, config_entry): + """Test config entry diagnostics.""" + with patch( + "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", + ) as mock_auth, patch( + "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + ), patch( + "homeassistant.components.netatmo.webhook_generate_url" + ): + mock_auth.return_value.async_post_request.side_effect = fake_post_request + mock_auth.return_value.async_addwebhook.side_effect = AsyncMock() + mock_auth.return_value.async_dropwebhook.side_effect = AsyncMock() + assert await async_setup_component(hass, "netatmo", {}) + + await hass.async_block_till_done() + + result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) + + # ignore for tests + result["info"]["data"]["token"].pop("expires_at") + result["info"].pop("entry_id") + + assert result["info"] == { + "data": { + "auth_implementation": "cloud", + "token": { + "access_token": "**REDACTED**", + "restricted_access_token": "**REDACTED**", + "expires_in": 60, + "refresh_token": "**REDACTED**", + "restricted_refresh_token": "**REDACTED**", + "scope": [ + "read_station", + "read_camera", + "access_camera", + "write_camera", + "read_presence", + "access_presence", + "write_presence", + "read_homecoach", + "read_smokedetector", + "read_thermostat", + "write_thermostat", + ], + "type": "Bearer", + }, + "webhook_id": "**REDACTED**", + }, + "disabled_by": None, + "domain": "netatmo", + "options": { + "weather_areas": { + "Home avg": { + "area_name": "Home avg", + "lat_ne": "**REDACTED**", + "lat_sw": "**REDACTED**", + "lon_ne": "**REDACTED**", + "lon_sw": "**REDACTED**", + "mode": "avg", + "show_on_map": False, + }, + "Home max": { + "area_name": "Home max", + "lat_ne": "**REDACTED**", + "lat_sw": "**REDACTED**", + "lon_ne": "**REDACTED**", + "lon_sw": "**REDACTED**", + "mode": "max", + "show_on_map": True, + }, + } + }, + "pref_disable_new_entities": False, + "pref_disable_polling": False, + "source": "user", + "title": "Mock Title", + "unique_id": "netatmo", + "version": 1, + "webhook_registered": False, + } + + assert result["data"]["AsyncClimate-111111111111111111111401"] is None + assert ( + result["data"]["AsyncClimate-91763b24c43d3e344f424e8b"]["repr"] + == "AsyncClimate(home_id=91763b24c43d3e344f424e8b)" + )