mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 13:47:35 +00:00
Improve ESPHome dashboard diagnostics (#143914)
This commit is contained in:
parent
dc02c37413
commit
4ee3290929
@ -10,10 +10,18 @@ from homeassistant.const import CONF_PASSWORD
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import CONF_NOISE_PSK
|
||||
from .const import CONF_DEVICE_NAME
|
||||
from .dashboard import async_get_dashboard
|
||||
from .entry_data import ESPHomeConfigEntry
|
||||
|
||||
REDACT_KEYS = {CONF_NOISE_PSK, CONF_PASSWORD, "mac_address", "bluetooth_mac_address"}
|
||||
CONFIGURED_DEVICE_KEYS = (
|
||||
"configuration",
|
||||
"current_version",
|
||||
"deployed_version",
|
||||
"loaded_integrations",
|
||||
"target_platform",
|
||||
)
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
@ -26,6 +34,9 @@ async def async_get_config_entry_diagnostics(
|
||||
|
||||
entry_data = config_entry.runtime_data
|
||||
device_info = entry_data.device_info
|
||||
device_name: str | None = (
|
||||
device_info.name if device_info else config_entry.data.get(CONF_DEVICE_NAME)
|
||||
)
|
||||
|
||||
if (storage_data := await entry_data.store.async_load()) is not None:
|
||||
diag["storage_data"] = storage_data
|
||||
@ -45,7 +56,19 @@ async def async_get_config_entry_diagnostics(
|
||||
"scanner": await scanner.async_diagnostics(),
|
||||
}
|
||||
|
||||
diag_dashboard: dict[str, Any] = {"configured": False}
|
||||
diag["dashboard"] = diag_dashboard
|
||||
if dashboard := async_get_dashboard(hass):
|
||||
diag["dashboard"] = dashboard.addon_slug
|
||||
diag_dashboard["configured"] = True
|
||||
diag_dashboard["supports_update"] = dashboard.supports_update
|
||||
diag_dashboard["last_update_success"] = dashboard.last_update_success
|
||||
diag_dashboard["last_exception"] = dashboard.last_exception
|
||||
diag_dashboard["addon"] = dashboard.addon_slug
|
||||
if device_name and dashboard.data:
|
||||
diag_dashboard["has_matching_name"] = device_name in dashboard.data
|
||||
if data := dashboard.data.get(device_name):
|
||||
diag_dashboard["device"] = {
|
||||
key: data.get(key) for key in CONFIGURED_DEVICE_KEYS
|
||||
}
|
||||
|
||||
return async_redact_data(diag, REDACT_KEYS)
|
||||
|
@ -1,15 +1,38 @@
|
||||
"""ESPHome test common code."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from homeassistant.components import assist_satellite
|
||||
from homeassistant.components.assist_satellite import AssistSatelliteEntity
|
||||
|
||||
# pylint: disable-next=hass-component-root-import
|
||||
from homeassistant.components.esphome import DOMAIN
|
||||
from homeassistant.components.esphome.assist_satellite import EsphomeAssistSatellite
|
||||
from homeassistant.components.esphome.coordinator import REFRESH_INTERVAL
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
class MockDashboardRefresh:
|
||||
"""Mock dashboard refresh."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize the mock dashboard refresh."""
|
||||
self.hass = hass
|
||||
self.last_time: datetime | None = None
|
||||
|
||||
async def async_refresh(self) -> None:
|
||||
"""Refresh the dashboard."""
|
||||
if self.last_time is None:
|
||||
self.last_time = dt_util.utcnow()
|
||||
self.last_time += REFRESH_INTERVAL
|
||||
async_fire_time_changed(self.hass, self.last_time)
|
||||
await self.hass.async_block_till_done()
|
||||
|
||||
|
||||
def get_satellite_entity(
|
||||
|
@ -26,6 +26,85 @@
|
||||
'unique_id': '11:22:33:44:55:aa',
|
||||
'version': 1,
|
||||
}),
|
||||
'dashboard': 'mock-slug',
|
||||
'dashboard': dict({
|
||||
'addon': 'mock-slug',
|
||||
'configured': True,
|
||||
'last_exception': None,
|
||||
'last_update_success': True,
|
||||
'supports_update': None,
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_diagnostics_with_dashboard_data
|
||||
dict({
|
||||
'config': dict({
|
||||
'data': dict({
|
||||
'device_name': 'test',
|
||||
'host': 'test.local',
|
||||
'password': '',
|
||||
'port': 6053,
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'discovery_keys': dict({
|
||||
}),
|
||||
'domain': 'esphome',
|
||||
'minor_version': 1,
|
||||
'options': dict({
|
||||
'allow_service_calls': False,
|
||||
}),
|
||||
'pref_disable_new_entities': False,
|
||||
'pref_disable_polling': False,
|
||||
'source': 'user',
|
||||
'subentries': list([
|
||||
]),
|
||||
'title': 'Mock Title',
|
||||
'unique_id': '11:22:33:44:55:aa',
|
||||
'version': 1,
|
||||
}),
|
||||
'dashboard': dict({
|
||||
'addon': 'mock-slug',
|
||||
'configured': True,
|
||||
'device': dict({
|
||||
'configuration': 'test.yaml',
|
||||
'current_version': '2023.1.0',
|
||||
'deployed_version': None,
|
||||
'loaded_integrations': None,
|
||||
'target_platform': None,
|
||||
}),
|
||||
'has_matching_name': True,
|
||||
'last_exception': None,
|
||||
'last_update_success': True,
|
||||
'supports_update': False,
|
||||
}),
|
||||
'storage_data': dict({
|
||||
'api_version': dict({
|
||||
'major': 99,
|
||||
'minor': 99,
|
||||
}),
|
||||
'device_info': dict({
|
||||
'bluetooth_mac_address': '',
|
||||
'bluetooth_proxy_feature_flags': 0,
|
||||
'compilation_time': '',
|
||||
'esphome_version': '1.0.0',
|
||||
'friendly_name': 'Test',
|
||||
'has_deep_sleep': False,
|
||||
'legacy_bluetooth_proxy_version': 0,
|
||||
'legacy_voice_assistant_version': 0,
|
||||
'mac_address': '**REDACTED**',
|
||||
'manufacturer': '',
|
||||
'model': '',
|
||||
'name': 'test',
|
||||
'project_name': '',
|
||||
'project_version': '',
|
||||
'suggested_area': '',
|
||||
'uses_password': False,
|
||||
'voice_assistant_feature_flags': 0,
|
||||
'webserver_port': 0,
|
||||
}),
|
||||
'services': list([
|
||||
]),
|
||||
'update': list([
|
||||
]),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
|
@ -1,6 +1,5 @@
|
||||
"""Test ESPHome dashboard features."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
@ -8,34 +7,16 @@ from aioesphomeapi import APIClient, DeviceInfo, InvalidAuthAPIError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.esphome import CONF_NOISE_PSK, DOMAIN, dashboard
|
||||
from homeassistant.components.esphome.coordinator import REFRESH_INTERVAL
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import VALID_NOISE_PSK
|
||||
from .common import MockDashboardRefresh
|
||||
from .conftest import MockESPHomeDeviceType
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
class MockDashboardRefresh:
|
||||
"""Mock dashboard refresh."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize the mock dashboard refresh."""
|
||||
self.hass = hass
|
||||
self.last_time: datetime | None = None
|
||||
|
||||
async def async_refresh(self) -> None:
|
||||
"""Refresh the dashboard."""
|
||||
if self.last_time is None:
|
||||
self.last_time = dt_util.utcnow()
|
||||
self.last_time += REFRESH_INTERVAL
|
||||
async_fire_time_changed(self.hass, self.last_time)
|
||||
await self.hass.async_block_till_done()
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration", "mock_dashboard")
|
||||
|
@ -3,6 +3,7 @@
|
||||
from typing import Any
|
||||
from unittest.mock import ANY
|
||||
|
||||
from aioesphomeapi import APIClient
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
from syrupy.filters import props
|
||||
@ -10,7 +11,8 @@ from syrupy.filters import props
|
||||
from homeassistant.components import bluetooth
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .conftest import MockESPHomeDevice
|
||||
from .common import MockDashboardRefresh
|
||||
from .conftest import MockESPHomeDevice, MockESPHomeDeviceType
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
@ -31,6 +33,37 @@ async def test_diagnostics(
|
||||
assert result == snapshot(exclude=props("created_at", "modified_at"))
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("enable_bluetooth")
|
||||
async def test_diagnostics_with_dashboard_data(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
mock_esphome_device: MockESPHomeDeviceType,
|
||||
mock_dashboard: dict[str, Any],
|
||||
mock_client: APIClient,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test diagnostics for config entry with dashboard data."""
|
||||
mock_dashboard["configured"].append(
|
||||
{
|
||||
"name": "test",
|
||||
"configuration": "test.yaml",
|
||||
"current_version": "2023.1.0",
|
||||
}
|
||||
)
|
||||
mock_device = await mock_esphome_device(
|
||||
mock_client=mock_client,
|
||||
entity_info=[],
|
||||
user_service=[],
|
||||
states=[],
|
||||
)
|
||||
await MockDashboardRefresh(hass).async_refresh()
|
||||
result = await get_diagnostics_for_config_entry(
|
||||
hass, hass_client, mock_device.entry
|
||||
)
|
||||
|
||||
assert result == snapshot(exclude=props("entry_id", "created_at", "modified_at"))
|
||||
|
||||
|
||||
async def test_diagnostics_with_bluetooth(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
@ -43,6 +76,9 @@ async def test_diagnostics_with_bluetooth(
|
||||
entry = mock_bluetooth_entry_with_raw_adv.entry
|
||||
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
|
||||
assert result == {
|
||||
"dashboard": {
|
||||
"configured": False,
|
||||
},
|
||||
"bluetooth": {
|
||||
"available": True,
|
||||
"connections_free": 0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user