diff --git a/homeassistant/components/proximity/diagnostics.py b/homeassistant/components/proximity/diagnostics.py new file mode 100644 index 00000000000..ba5e1f53722 --- /dev/null +++ b/homeassistant/components/proximity/diagnostics.py @@ -0,0 +1,49 @@ +"""Diagnostics support for Proximity.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.device_tracker import ATTR_GPS, ATTR_IP, ATTR_MAC +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.components.person import ATTR_USER_ID +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE +from homeassistant.core import HomeAssistant + +from .const import DOMAIN +from .coordinator import ProximityDataUpdateCoordinator + +TO_REDACT = { + ATTR_GPS, + ATTR_IP, + ATTR_LATITUDE, + ATTR_LONGITUDE, + ATTR_MAC, + ATTR_USER_ID, + "context", +} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + coordinator: ProximityDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + + diag_data = { + "entry": entry.as_dict(), + } + + tracked_states: dict[str, dict] = {} + for tracked_entity_id in coordinator.tracked_entities: + if (state := hass.states.get(tracked_entity_id)) is None: + continue + tracked_states[tracked_entity_id] = state.as_dict() + + diag_data["data"] = { + "proximity": coordinator.data.proximity, + "entities": coordinator.data.entities, + "entity_mapping": coordinator.entity_mapping, + "tracked_states": async_redact_data(tracked_states, TO_REDACT), + } + return diag_data diff --git a/tests/components/proximity/snapshots/test_diagnostics.ambr b/tests/components/proximity/snapshots/test_diagnostics.ambr new file mode 100644 index 00000000000..f8f7d9b014e --- /dev/null +++ b/tests/components/proximity/snapshots/test_diagnostics.ambr @@ -0,0 +1,86 @@ +# serializer version: 1 +# name: test_entry_diagnostics + dict({ + 'data': dict({ + 'entities': dict({ + 'device_tracker.test1': dict({ + 'dir_of_travel': None, + 'dist_to_zone': 2218752, + 'is_in_ignored_zone': False, + 'name': 'test1', + }), + 'device_tracker.test2': dict({ + 'dir_of_travel': None, + 'dist_to_zone': 4077309, + 'is_in_ignored_zone': False, + 'name': 'test2', + }), + }), + 'entity_mapping': dict({ + 'device_tracker.test1': list([ + 'sensor.home_test1_distance', + 'sensor.home_test1_direction_of_travel', + ]), + 'device_tracker.test2': list([ + 'sensor.home_test2_distance', + 'sensor.home_test2_direction_of_travel', + ]), + 'device_tracker.test3': list([ + 'sensor.home_test3_distance', + 'sensor.home_test3_direction_of_travel', + ]), + }), + 'proximity': dict({ + 'dir_of_travel': 'unknown', + 'dist_to_zone': 2219, + 'nearest': 'test1', + }), + 'tracked_states': dict({ + 'device_tracker.test1': dict({ + 'attributes': dict({ + 'friendly_name': 'test1', + 'latitude': '**REDACTED**', + 'longitude': '**REDACTED**', + }), + 'context': '**REDACTED**', + 'entity_id': 'device_tracker.test1', + 'state': 'not_home', + }), + 'device_tracker.test2': dict({ + 'attributes': dict({ + 'friendly_name': 'test2', + 'latitude': '**REDACTED**', + 'longitude': '**REDACTED**', + }), + 'context': '**REDACTED**', + 'entity_id': 'device_tracker.test2', + 'state': 'not_home', + }), + }), + }), + 'entry': dict({ + 'data': dict({ + 'ignored_zones': list([ + ]), + 'tolerance': 1, + 'tracked_entities': list([ + 'device_tracker.test1', + 'device_tracker.test2', + 'device_tracker.test3', + ]), + 'zone': 'zone.home', + }), + 'disabled_by': None, + 'domain': 'proximity', + 'minor_version': 1, + 'options': dict({ + }), + 'pref_disable_new_entities': False, + 'pref_disable_polling': False, + 'source': 'user', + 'title': 'home', + 'unique_id': 'proximity_home', + 'version': 1, + }), + }) +# --- diff --git a/tests/components/proximity/test_diagnostics.py b/tests/components/proximity/test_diagnostics.py new file mode 100644 index 00000000000..35ecd152a06 --- /dev/null +++ b/tests/components/proximity/test_diagnostics.py @@ -0,0 +1,62 @@ +"""Tests for proximity diagnostics platform.""" +from __future__ import annotations + +from syrupy.assertion import SnapshotAssertion +from syrupy.filters import props + +from homeassistant.components.proximity.const import ( + CONF_IGNORED_ZONES, + CONF_TOLERANCE, + CONF_TRACKED_ENTITIES, + DOMAIN, +) +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import CONF_ZONE +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry +from tests.components.diagnostics import get_diagnostics_for_config_entry +from tests.typing import ClientSessionGenerator + + +async def test_entry_diagnostics( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + snapshot: SnapshotAssertion, +) -> None: + """Test config entry diagnostics.""" + hass.states.async_set( + "device_tracker.test1", + "not_home", + {"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1}, + ) + hass.states.async_set( + "device_tracker.test2", + "not_home", + {"friendly_name": "test2", "latitude": 150.1, "longitude": 20.1}, + ) + + mock_entry = MockConfigEntry( + domain=DOMAIN, + title="home", + data={ + CONF_ZONE: "zone.home", + CONF_TRACKED_ENTITIES: [ + "device_tracker.test1", + "device_tracker.test2", + "device_tracker.test3", + ], + CONF_IGNORED_ZONES: [], + CONF_TOLERANCE: 1, + }, + unique_id=f"{DOMAIN}_home", + ) + + mock_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + assert mock_entry.state == ConfigEntryState.LOADED + + assert await get_diagnostics_for_config_entry( + hass, hass_client, mock_entry + ) == snapshot(exclude=props("entry_id", "last_changed", "last_updated"))