mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Quality improvements for the ESPHome dashboard coordinator (#143619)
This commit is contained in:
parent
2abe2f7d59
commit
fab70a80bb
@ -5,43 +5,38 @@ from __future__ import annotations
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import aiohttp
|
||||
from awesomeversion import AwesomeVersion
|
||||
from esphome_dashboard_api import ConfiguredDevice, ESPHomeDashboardAPI
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MIN_VERSION_SUPPORTS_UPDATE = AwesomeVersion("2023.1.0")
|
||||
REFRESH_INTERVAL = timedelta(minutes=5)
|
||||
|
||||
|
||||
class ESPHomeDashboardCoordinator(DataUpdateCoordinator[dict[str, ConfiguredDevice]]):
|
||||
"""Class to interact with the ESPHome dashboard."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
addon_slug: str,
|
||||
url: str,
|
||||
session: aiohttp.ClientSession,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
def __init__(self, hass: HomeAssistant, addon_slug: str, url: str) -> None:
|
||||
"""Initialize the dashboard coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
config_entry=None,
|
||||
name="ESPHome Dashboard",
|
||||
update_interval=timedelta(minutes=5),
|
||||
update_interval=REFRESH_INTERVAL,
|
||||
always_update=False,
|
||||
)
|
||||
self.addon_slug = addon_slug
|
||||
self.url = url
|
||||
self.api = ESPHomeDashboardAPI(url, session)
|
||||
self.api = ESPHomeDashboardAPI(url, async_get_clientsession(hass))
|
||||
self.supports_update: bool | None = None
|
||||
|
||||
async def _async_update_data(self) -> dict:
|
||||
async def _async_update_data(self) -> dict[str, ConfiguredDevice]:
|
||||
"""Fetch device data."""
|
||||
devices = await self.api.get_devices()
|
||||
configured_devices = devices["configured"]
|
||||
|
@ -9,7 +9,6 @@ from typing import Any
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.hassio import is_hassio
|
||||
from homeassistant.helpers.singleton import singleton
|
||||
from homeassistant.helpers.storage import Store
|
||||
@ -104,9 +103,7 @@ class ESPHomeDashboardManager:
|
||||
self._cancel_shutdown = None
|
||||
self._current_dashboard = None
|
||||
|
||||
dashboard = ESPHomeDashboardCoordinator(
|
||||
hass, addon_slug, url, async_get_clientsession(hass)
|
||||
)
|
||||
dashboard = ESPHomeDashboardCoordinator(hass, addon_slug, url)
|
||||
await dashboard.async_request_refresh()
|
||||
|
||||
self._current_dashboard = dashboard
|
||||
|
@ -70,7 +70,6 @@ async def async_setup_entry(
|
||||
@callback
|
||||
def _async_setup_update_entity() -> None:
|
||||
"""Set up the update entity."""
|
||||
nonlocal unsubs
|
||||
assert dashboard is not None
|
||||
# Keep listening until device is available
|
||||
if not entry_data.available or not dashboard.last_update_success:
|
||||
@ -95,10 +94,12 @@ async def async_setup_entry(
|
||||
_async_setup_update_entity()
|
||||
return
|
||||
|
||||
unsubs = [
|
||||
entry_data.async_subscribe_device_updated(_async_setup_update_entity),
|
||||
dashboard.async_add_listener(_async_setup_update_entity),
|
||||
]
|
||||
unsubs.extend(
|
||||
[
|
||||
entry_data.async_subscribe_device_updated(_async_setup_update_entity),
|
||||
dashboard.async_add_listener(_async_setup_update_entity),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class ESPHomeDashboardUpdateEntity(
|
||||
|
@ -1,26 +1,46 @@
|
||||
"""Test ESPHome dashboard features."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from aioesphomeapi import DeviceInfo, InvalidAuthAPIError
|
||||
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 .conftest import MockESPHomeDeviceType
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
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()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration", "mock_dashboard")
|
||||
async def test_dashboard_storage(
|
||||
hass: HomeAssistant,
|
||||
init_integration,
|
||||
mock_dashboard: dict[str, Any],
|
||||
hass_storage: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test dashboard storage."""
|
||||
@ -165,8 +185,9 @@ async def test_setup_dashboard_fails_when_already_setup(
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_dashboard")
|
||||
async def test_new_info_reload_config_entries(
|
||||
hass: HomeAssistant, init_integration, mock_dashboard
|
||||
hass: HomeAssistant, init_integration: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test config entries are reloaded when new info is set."""
|
||||
assert init_integration.state is ConfigEntryState.LOADED
|
||||
@ -185,7 +206,10 @@ async def test_new_info_reload_config_entries(
|
||||
|
||||
|
||||
async def test_new_dashboard_fix_reauth(
|
||||
hass: HomeAssistant, mock_client, mock_config_entry: MockConfigEntry, mock_dashboard
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_dashboard: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test config entries waiting for reauth are triggered."""
|
||||
mock_client.device_info.side_effect = (
|
||||
@ -209,7 +233,7 @@ async def test_new_dashboard_fix_reauth(
|
||||
}
|
||||
)
|
||||
|
||||
await dashboard.async_get_dashboard(hass).async_refresh()
|
||||
await MockDashboardRefresh(hass).async_refresh()
|
||||
|
||||
with (
|
||||
patch(
|
||||
@ -229,15 +253,29 @@ async def test_new_dashboard_fix_reauth(
|
||||
|
||||
|
||||
async def test_dashboard_supports_update(
|
||||
hass: HomeAssistant, mock_dashboard: dict[str, Any]
|
||||
hass: HomeAssistant,
|
||||
mock_dashboard: dict[str, Any],
|
||||
mock_client: APIClient,
|
||||
mock_esphome_device: MockESPHomeDeviceType,
|
||||
) -> None:
|
||||
"""Test dashboard supports update."""
|
||||
dash = dashboard.async_get_dashboard(hass)
|
||||
mock_refresh = MockDashboardRefresh(hass)
|
||||
|
||||
entity_info = []
|
||||
states = []
|
||||
user_service = []
|
||||
await mock_esphome_device(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
user_service=user_service,
|
||||
states=states,
|
||||
)
|
||||
|
||||
# No data
|
||||
assert not dash.supports_update
|
||||
|
||||
await dash.async_refresh()
|
||||
await mock_refresh.async_refresh()
|
||||
assert dash.supports_update is None
|
||||
|
||||
# supported version
|
||||
@ -248,12 +286,44 @@ async def test_dashboard_supports_update(
|
||||
"current_version": "2023.2.0-dev",
|
||||
}
|
||||
)
|
||||
await dash.async_refresh()
|
||||
|
||||
await mock_refresh.async_refresh()
|
||||
assert dash.supports_update is True
|
||||
|
||||
# unsupported version
|
||||
dash.supports_update = None
|
||||
mock_dashboard["configured"][0]["current_version"] = "2023.1.0"
|
||||
await dash.async_refresh()
|
||||
|
||||
async def test_dashboard_unsupported_version(
|
||||
hass: HomeAssistant,
|
||||
mock_dashboard: dict[str, Any],
|
||||
mock_client: APIClient,
|
||||
mock_esphome_device: MockESPHomeDeviceType,
|
||||
) -> None:
|
||||
"""Test dashboard with unsupported version."""
|
||||
dash = dashboard.async_get_dashboard(hass)
|
||||
mock_refresh = MockDashboardRefresh(hass)
|
||||
|
||||
entity_info = []
|
||||
states = []
|
||||
user_service = []
|
||||
await mock_esphome_device(
|
||||
mock_client=mock_client,
|
||||
entity_info=entity_info,
|
||||
user_service=user_service,
|
||||
states=states,
|
||||
)
|
||||
|
||||
# No data
|
||||
assert not dash.supports_update
|
||||
|
||||
await mock_refresh.async_refresh()
|
||||
assert dash.supports_update is None
|
||||
|
||||
# unsupported version
|
||||
mock_dashboard["configured"].append(
|
||||
{
|
||||
"name": "test",
|
||||
"configuration": "test.yaml",
|
||||
"current_version": "2023.1.0",
|
||||
}
|
||||
)
|
||||
await mock_refresh.async_refresh()
|
||||
assert dash.supports_update is False
|
||||
|
Loading…
x
Reference in New Issue
Block a user