Compare commits

...

2 Commits

Author SHA1 Message Date
Mike Degatano f3104fc3ea Change supervisor_update_pending text 2026-05-28 21:02:29 +00:00
Mike Degatano 5d26255e08 Ensure Supervisor is up to date before hassio setup completes
During onboarding, Supervisor may be out of date relative to the core
version being installed. Attempt a Supervisor update at the start of
async_setup_entry when not yet onboarded:

- SupervisorBadRequestError: no update available, proceed normally.
- No exception: update was triggered, raise ConfigEntryNotReady to
  retry once Supervisor has restarted with the new version.
- Any other SupervisorError: unexpected failure communicating with
  Supervisor, raise ConfigEntryNotReady to retry.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 21:01:09 +00:00
3 changed files with 89 additions and 2 deletions
+24 -1
View File
@@ -8,7 +8,7 @@ import os
import struct
from typing import Any
from aiohasupervisor import SupervisorError
from aiohasupervisor import SupervisorBadRequestError, SupervisorError
from aiohasupervisor.models import (
GreenOptions,
HomeAssistantOptions,
@@ -25,6 +25,7 @@ from homeassistant.components.http import (
CONF_SERVER_PORT,
CONF_SSL_CERTIFICATE,
)
from homeassistant.components.onboarding import async_is_onboarded
from homeassistant.config_entries import SOURCE_SYSTEM, ConfigEntry
from homeassistant.const import (
EVENT_CORE_CONFIG_UPDATE,
@@ -301,6 +302,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
translation_key="supervisor_not_connected",
) from err
# During onboarding, Supervisor may be out of date. Attempt an update now
# so that core loads against an up-to-date Supervisor. A
# SupervisorBadRequestError means there is no update available, proceed
# normally. No exception means an update was triggered and we must wait for
# it to complete. Any other SupervisorError means something unexpected went
# wrong and we cannot proceed right now.
if not async_is_onboarded(hass):
try:
await supervisor_client.supervisor.update()
except SupervisorBadRequestError:
pass # No update available, proceed normally.
except SupervisorError as err:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="supervisor_not_connected",
) from err
else:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="supervisor_update_pending",
)
# Get or create a refresh token for the Supervisor user
user = hass.data[DATA_HASSIO_SUPERVISOR_USER]
if user.refresh_tokens:
@@ -55,6 +55,9 @@
},
"supervisor_not_connected": {
"message": "Not connected with the supervisor / system too busy"
},
"supervisor_update_pending": {
"message": "Supervisor was out-of-date during onboarding. Update triggered, will retry when complete"
}
},
"issues": {
+62 -1
View File
@@ -8,7 +8,7 @@ from typing import Any
from unittest.mock import ANY, AsyncMock, Mock, call, patch
from uuid import uuid4
from aiohasupervisor import SupervisorError
from aiohasupervisor import SupervisorBadRequestError, SupervisorError
from aiohasupervisor.models import (
AddonsStats,
AddonStage,
@@ -191,6 +191,67 @@ async def test_setup_api_ping_fails(
assert entry.state is ConfigEntryState.SETUP_RETRY
async def test_setup_onboarding_supervisor_update(
hass: HomeAssistant,
supervisor_client: AsyncMock,
) -> None:
"""Test that during onboarding, supervisor.update() success triggers retry."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch("homeassistant.components.hassio.async_is_onboarded", return_value=False),
):
result = await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
assert result
assert is_hassio(hass)
entry = hass.config_entries.async_entries("hassio")[0]
assert entry.state is ConfigEntryState.SETUP_RETRY
supervisor_client.supervisor.update.assert_called_once()
async def test_setup_onboarding_supervisor_no_update(
hass: HomeAssistant,
supervisor_client: AsyncMock,
) -> None:
"""Test that during onboarding, SupervisorBadRequestError means no update needed."""
supervisor_client.supervisor.update.side_effect = SupervisorBadRequestError
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch("homeassistant.components.hassio.async_is_onboarded", return_value=False),
):
result = await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
assert result
assert is_hassio(hass)
entry = hass.config_entries.async_entries("hassio")[0]
assert entry.state is ConfigEntryState.LOADED
supervisor_client.supervisor.update.assert_called_once()
async def test_setup_onboarding_supervisor_update_error(
hass: HomeAssistant,
supervisor_client: AsyncMock,
) -> None:
"""Test that during onboarding, an unknown SupervisorError causes retry."""
supervisor_client.supervisor.update.side_effect = SupervisorError
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch("homeassistant.components.hassio.async_is_onboarded", return_value=False),
):
result = await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
assert result
assert is_hassio(hass)
entry = hass.config_entries.async_entries("hassio")[0]
assert entry.state is ConfigEntryState.SETUP_RETRY
supervisor_client.supervisor.update.assert_called_once()
async def test_setup_app_panel(hass: HomeAssistant) -> None:
"""Test app panel is registered."""
with patch.dict(os.environ, MOCK_ENVIRON):