diff --git a/homeassistant/components/airvisual/diagnostics.py b/homeassistant/components/airvisual/diagnostics.py new file mode 100644 index 00000000000..a2c131ce3ca --- /dev/null +++ b/homeassistant/components/airvisual/diagnostics.py @@ -0,0 +1,57 @@ +"""Diagnostics support for AirVisual.""" +from __future__ import annotations + +from types import MappingProxyType +from typing import Any + +from homeassistant.components.diagnostics import REDACTED +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_STATE +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import CONF_CITY, CONF_COUNTRY, DOMAIN + +ATTR_DATA = "data" +ATTR_OPTIONS = "options" +ATTR_TITLE = "title" + +CONF_COORDINATES = "coordinates" + + +@callback +def _async_redact_data(data: MappingProxyType | dict) -> dict[str, Any]: + """Redact sensitive data in a dict.""" + redacted = {**data} + + for key, value in redacted.items(): + if key in ( + CONF_API_KEY, + CONF_CITY, + CONF_COORDINATES, + CONF_COUNTRY, + CONF_LATITUDE, + CONF_LONGITUDE, + CONF_STATE, + ): + redacted[key] = REDACTED + if isinstance(value, dict): + redacted[key] = _async_redact_data(value) + + return redacted + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + + return { + "entry": { + "title": entry.title, + "data": _async_redact_data(entry.data), + "options": _async_redact_data(entry.options), + }, + "data": _async_redact_data(coordinator.data["data"]), + } diff --git a/tests/components/airvisual/conftest.py b/tests/components/airvisual/conftest.py index 119a188066d..81b22f19cc5 100644 --- a/tests/components/airvisual/conftest.py +++ b/tests/components/airvisual/conftest.py @@ -1,4 +1,5 @@ """Define test fixtures for AirVisual.""" +import json from unittest.mock import patch import pytest @@ -16,7 +17,7 @@ from homeassistant.const import ( ) from homeassistant.setup import async_setup_component -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, load_fixture @pytest.fixture(name="config_entry") @@ -49,11 +50,17 @@ def config_fixture(hass): } +@pytest.fixture(name="data", scope="session") +def data_fixture(): + """Define an update coordinator data example.""" + return json.loads(load_fixture("data.json", "airvisual")) + + @pytest.fixture(name="setup_airvisual") -async def setup_airvisual_fixture(hass, config): +async def setup_airvisual_fixture(hass, config, data): """Define a fixture to set up AirVisual.""" with patch("pyairvisual.air_quality.AirQuality.city"), patch( - "pyairvisual.air_quality.AirQuality.nearest_city" + "pyairvisual.air_quality.AirQuality.nearest_city", return_value=data ), patch("pyairvisual.node.NodeSamba.async_connect"), patch( "pyairvisual.node.NodeSamba.async_get_latest_measurements" ), patch( diff --git a/tests/components/airvisual/fixtures/data.json b/tests/components/airvisual/fixtures/data.json new file mode 100644 index 00000000000..ff955e5528e --- /dev/null +++ b/tests/components/airvisual/fixtures/data.json @@ -0,0 +1,33 @@ +{ + "status": "success", + "data": { + "city": "Edgewater", + "state": "Colorado", + "country": "USA", + "location": { + "type": "Point", + "coordinates": [ + -105.06415, + 39.75304 + ] + }, + "current": { + "weather": { + "ts": "2021-09-03T21:00:00.000Z", + "tp": 23, + "pr": 999, + "hu": 45, + "ws": 0.45, + "wd": 252, + "ic": "10d" + }, + "pollution": { + "ts": "2021-09-04T00:00:00.000Z", + "aqius": 52, + "mainus": "p2", + "aqicn": 18, + "maincn": "p2" + } + } + } +} diff --git a/tests/components/airvisual/test_diagnostics.py b/tests/components/airvisual/test_diagnostics.py new file mode 100644 index 00000000000..846b289101a --- /dev/null +++ b/tests/components/airvisual/test_diagnostics.py @@ -0,0 +1,47 @@ +"""Test evil genius labs diagnostics.""" +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_entry_diagnostics(hass, config_entry, hass_client, setup_airvisual): + """Test config entry diagnostics.""" + assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == { + "entry": { + "title": "Mock Title", + "data": { + "api_key": "**REDACTED**", + "integration_type": "Geographical Location by Latitude/Longitude", + "latitude": "**REDACTED**", + "longitude": "**REDACTED**", + }, + "options": { + "show_on_map": True, + }, + }, + "data": { + "city": "**REDACTED**", + "country": "**REDACTED**", + "current": { + "weather": { + "ts": "2021-09-03T21:00:00.000Z", + "tp": 23, + "pr": 999, + "hu": 45, + "ws": 0.45, + "wd": 252, + "ic": "10d", + }, + "pollution": { + "ts": "2021-09-04T00:00:00.000Z", + "aqius": 52, + "mainus": "p2", + "aqicn": 18, + "maincn": "p2", + }, + }, + "location": { + "coordinates": "**REDACTED**", + "type": "Point", + }, + "state": "**REDACTED**", + }, + }