Retry creating esphome update entities later if dashboard is unavailable (#92042)
This commit is contained in:
parent
f33e8c518f
commit
ec5f50913a
2 changed files with 65 additions and 19 deletions
|
@ -13,7 +13,7 @@ from homeassistant.components.update import (
|
||||||
UpdateEntityFeature,
|
UpdateEntityFeature,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
@ -33,35 +33,36 @@ async def async_setup_entry(
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up ESPHome update based on a config entry."""
|
"""Set up ESPHome update based on a config entry."""
|
||||||
dashboard = async_get_dashboard(hass)
|
if (dashboard := async_get_dashboard(hass)) is None:
|
||||||
|
|
||||||
if dashboard is None:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
entry_data = DomainData.get(hass).get_entry_data(entry)
|
entry_data = DomainData.get(hass).get_entry_data(entry)
|
||||||
unsub = None
|
unsubs: list[CALLBACK_TYPE] = []
|
||||||
|
|
||||||
async def setup_update_entity() -> None:
|
@callback
|
||||||
|
def _async_setup_update_entity() -> None:
|
||||||
"""Set up the update entity."""
|
"""Set up the update entity."""
|
||||||
nonlocal unsub
|
nonlocal unsubs
|
||||||
|
assert dashboard is not None
|
||||||
# Keep listening until device is available
|
# Keep listening until device is available
|
||||||
if not entry_data.available:
|
if not entry_data.available or not dashboard.last_update_success:
|
||||||
return
|
return
|
||||||
|
|
||||||
if unsub is not None:
|
for unsub in unsubs:
|
||||||
unsub() # type: ignore[unreachable]
|
unsub()
|
||||||
|
unsubs.clear()
|
||||||
|
|
||||||
assert dashboard is not None
|
|
||||||
async_add_entities([ESPHomeUpdateEntity(entry_data, dashboard)])
|
async_add_entities([ESPHomeUpdateEntity(entry_data, dashboard)])
|
||||||
|
|
||||||
if entry_data.available:
|
if entry_data.available and dashboard.last_update_success:
|
||||||
await setup_update_entity()
|
_async_setup_update_entity()
|
||||||
return
|
return
|
||||||
|
|
||||||
unsub = async_dispatcher_connect(
|
unsubs = [
|
||||||
hass, entry_data.signal_device_updated, setup_update_entity
|
async_dispatcher_connect(
|
||||||
)
|
hass, entry_data.signal_device_updated, _async_setup_update_entity
|
||||||
|
),
|
||||||
|
dashboard.async_add_listener(_async_setup_update_entity),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ESPHomeUpdateEntity(CoordinatorEntity[ESPHomeDashboard], UpdateEntity):
|
class ESPHomeUpdateEntity(CoordinatorEntity[ESPHomeDashboard], UpdateEntity):
|
||||||
|
@ -88,7 +89,11 @@ class ESPHomeUpdateEntity(CoordinatorEntity[ESPHomeDashboard], UpdateEntity):
|
||||||
|
|
||||||
# If the device has deep sleep, we can't assume we can install updates
|
# If the device has deep sleep, we can't assume we can install updates
|
||||||
# as the ESP will not be connectable (by design).
|
# as the ESP will not be connectable (by design).
|
||||||
if coordinator.supports_update and not self._device_info.has_deep_sleep:
|
if (
|
||||||
|
coordinator.last_update_success
|
||||||
|
and coordinator.supports_update
|
||||||
|
and not self._device_info.has_deep_sleep
|
||||||
|
):
|
||||||
self._attr_supported_features = UpdateEntityFeature.INSTALL
|
self._attr_supported_features = UpdateEntityFeature.INSTALL
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Test ESPHome update entities."""
|
"""Test ESPHome update entities."""
|
||||||
|
import asyncio
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
@ -197,3 +198,43 @@ async def test_update_device_state_for_availability(
|
||||||
|
|
||||||
state = hass.states.get("update.none_firmware")
|
state = hass.states.get("update.none_firmware")
|
||||||
assert state.state == "on"
|
assert state.state == "on"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_update_entity_dashboard_not_available_startup(
|
||||||
|
hass: HomeAssistant, mock_config_entry, mock_device_info, mock_dashboard
|
||||||
|
) -> None:
|
||||||
|
"""Test ESPHome update entity when dashboard is not available at startup."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.esphome.update.DomainData.get_entry_data",
|
||||||
|
return_value=Mock(available=True, device_info=mock_device_info),
|
||||||
|
), patch(
|
||||||
|
"esphome_dashboard_api.ESPHomeDashboardAPI.get_devices",
|
||||||
|
side_effect=asyncio.TimeoutError,
|
||||||
|
):
|
||||||
|
await async_get_dashboard(hass).async_refresh()
|
||||||
|
assert await hass.config_entries.async_forward_entry_setup(
|
||||||
|
mock_config_entry, "update"
|
||||||
|
)
|
||||||
|
|
||||||
|
state = hass.states.get("update.none_firmware")
|
||||||
|
assert state is None
|
||||||
|
|
||||||
|
mock_dashboard["configured"] = [
|
||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"current_version": "2023.2.0-dev",
|
||||||
|
"configuration": "test.yaml",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
await async_get_dashboard(hass).async_refresh()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("update.none_firmware")
|
||||||
|
assert state.state == "on"
|
||||||
|
expected_attributes = {
|
||||||
|
"latest_version": "2023.2.0-dev",
|
||||||
|
"installed_version": "1.0.0",
|
||||||
|
"supported_features": UpdateEntityFeature.INSTALL,
|
||||||
|
}
|
||||||
|
for key, expected_value in expected_attributes.items():
|
||||||
|
assert state.attributes.get(key) == expected_value
|
||||||
|
|
Loading…
Add table
Reference in a new issue