diff --git a/homeassistant/components/amazon_devices/diagnostics.py b/homeassistant/components/amazon_devices/diagnostics.py new file mode 100644 index 00000000000..e9a0773cd3f --- /dev/null +++ b/homeassistant/components/amazon_devices/diagnostics.py @@ -0,0 +1,66 @@ +"""Diagnostics support for Amazon Devices integration.""" + +from __future__ import annotations + +from typing import Any + +from aioamazondevices.api import AmazonDevice + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceEntry + +from .coordinator import AmazonConfigEntry + +TO_REDACT = {CONF_PASSWORD, CONF_USERNAME, CONF_NAME, "title"} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: AmazonConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + + coordinator = entry.runtime_data + + devices: list[dict[str, dict[str, Any]]] = [ + build_device_data(device) for device in coordinator.data.values() + ] + + return { + "entry": async_redact_data(entry.as_dict(), TO_REDACT), + "device_info": { + "last_update success": coordinator.last_update_success, + "last_exception": repr(coordinator.last_exception), + "devices": devices, + }, + } + + +async def async_get_device_diagnostics( + hass: HomeAssistant, entry: AmazonConfigEntry, device_entry: DeviceEntry +) -> dict[str, Any]: + """Return diagnostics for a device.""" + + coordinator = entry.runtime_data + + assert device_entry.serial_number + + return build_device_data(coordinator.data[device_entry.serial_number]) + + +def build_device_data(device: AmazonDevice) -> dict[str, Any]: + """Build device data for diagnostics.""" + return { + "account name": device.account_name, + "capabilities": device.capabilities, + "device family": device.device_family, + "device type": device.device_type, + "device cluster members": device.device_cluster_members, + "online": device.online, + "serial number": device.serial_number, + "software version": device.software_version, + "do not disturb": device.do_not_disturb, + "response style": device.response_style, + "bluetooth state": device.bluetooth_state, + } diff --git a/tests/components/amazon_devices/snapshots/test_diagnostics.ambr b/tests/components/amazon_devices/snapshots/test_diagnostics.ambr new file mode 100644 index 00000000000..0b5164418aa --- /dev/null +++ b/tests/components/amazon_devices/snapshots/test_diagnostics.ambr @@ -0,0 +1,74 @@ +# serializer version: 1 +# name: test_device_diagnostics + dict({ + 'account name': 'Echo Test', + 'bluetooth state': True, + 'capabilities': list([ + 'AUDIO_PLAYER', + 'MICROPHONE', + ]), + 'device cluster members': list([ + 'echo_test_serial_number', + ]), + 'device family': 'mine', + 'device type': 'echo', + 'do not disturb': False, + 'online': True, + 'response style': None, + 'serial number': 'echo_test_serial_number', + 'software version': 'echo_test_software_version', + }) +# --- +# name: test_entry_diagnostics + dict({ + 'device_info': dict({ + 'devices': list([ + dict({ + 'account name': 'Echo Test', + 'bluetooth state': True, + 'capabilities': list([ + 'AUDIO_PLAYER', + 'MICROPHONE', + ]), + 'device cluster members': list([ + 'echo_test_serial_number', + ]), + 'device family': 'mine', + 'device type': 'echo', + 'do not disturb': False, + 'online': True, + 'response style': None, + 'serial number': 'echo_test_serial_number', + 'software version': 'echo_test_software_version', + }), + ]), + 'last_exception': 'None', + 'last_update success': True, + }), + 'entry': dict({ + 'data': dict({ + 'country': 'IT', + 'login_data': dict({ + 'session': 'test-session', + }), + 'password': '**REDACTED**', + 'username': '**REDACTED**', + }), + 'disabled_by': None, + 'discovery_keys': dict({ + }), + 'domain': 'amazon_devices', + 'minor_version': 1, + 'options': dict({ + }), + 'pref_disable_new_entities': False, + 'pref_disable_polling': False, + 'source': 'user', + 'subentries': list([ + ]), + 'title': '**REDACTED**', + 'unique_id': 'fake_email@gmail.com', + 'version': 1, + }), + }) +# --- diff --git a/tests/components/amazon_devices/test_diagnostics.py b/tests/components/amazon_devices/test_diagnostics.py new file mode 100644 index 00000000000..e548702650b --- /dev/null +++ b/tests/components/amazon_devices/test_diagnostics.py @@ -0,0 +1,70 @@ +"""Tests for Amazon Devices diagnostics platform.""" + +from __future__ import annotations + +from unittest.mock import AsyncMock + +from syrupy.assertion import SnapshotAssertion +from syrupy.filters import props + +from homeassistant.components.amazon_devices.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr + +from . import setup_integration +from .const import TEST_SERIAL_NUMBER + +from tests.common import MockConfigEntry +from tests.components.diagnostics import ( + get_diagnostics_for_config_entry, + get_diagnostics_for_device, +) +from tests.typing import ClientSessionGenerator + + +async def test_entry_diagnostics( + hass: HomeAssistant, + mock_amazon_devices_client: AsyncMock, + mock_config_entry: MockConfigEntry, + hass_client: ClientSessionGenerator, + snapshot: SnapshotAssertion, +) -> None: + """Test Amazon config entry diagnostics.""" + await setup_integration(hass, mock_config_entry) + + assert await get_diagnostics_for_config_entry( + hass, hass_client, mock_config_entry + ) == snapshot( + exclude=props( + "entry_id", + "created_at", + "modified_at", + ) + ) + + +async def test_device_diagnostics( + hass: HomeAssistant, + mock_amazon_devices_client: AsyncMock, + mock_config_entry: MockConfigEntry, + hass_client: ClientSessionGenerator, + device_registry: dr.DeviceRegistry, + snapshot: SnapshotAssertion, +) -> None: + """Test Amazon device diagnostics.""" + await setup_integration(hass, mock_config_entry) + + device = device_registry.async_get_device( + identifiers={(DOMAIN, TEST_SERIAL_NUMBER)} + ) + assert device, repr(device_registry.devices) + + assert await get_diagnostics_for_device( + hass, hass_client, mock_config_entry, device + ) == snapshot( + exclude=props( + "entry_id", + "created_at", + "modified_at", + ) + )