Refresh add-on update entities after store reload through Supervisor API proxy (#176648)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Paulus Schoutsen
2026-07-17 18:14:07 +02:00
committed by GitHub
parent 7f493398c9
commit 24a1d59562
3 changed files with 69 additions and 0 deletions
@@ -1411,6 +1411,11 @@ class HassioAddOnDataUpdateCoordinator(DataUpdateCoordinator[HassioAddonData]):
log_failures, raise_on_auth_failed, scheduled, raise_on_entry_error
)
async def async_refresh_after_store_reload(self) -> None:
"""Refresh addon data when the store was already reloaded externally."""
async with self._debounced_refresh.async_lock():
await super()._async_refresh(log_failures=True)
async def force_addon_info_data_refresh(self, addon_slug: str) -> None:
"""Force refresh of addon info data for a specific addon."""
try:
@@ -20,6 +20,7 @@ from homeassistant.helpers.dispatcher import (
from .config import HassioUpdateParametersDict
from .const import (
ADDONS_COORDINATOR,
ATTR_DATA,
ATTR_ENDPOINT,
ATTR_METHOD,
@@ -59,6 +60,10 @@ WS_NO_ADMIN_ENDPOINTS = re.compile(
r")$"
)
# Endpoint that reloads the add-on store. Afterwards the add-on update
# entities must be refreshed so they don't report stale update information.
STORE_RELOAD_ENDPOINT = "/store/reload"
_LOGGER: logging.Logger = logging.getLogger(__package__)
@@ -159,6 +164,15 @@ async def websocket_supervisor_api(
# sensitive information and the frontend does not require it for ingress.
if not connection.user.is_admin and WS_ADDONS_INFO_ENDPOINT.match(command):
data.pop("options", None)
# Await so the frontend only sees the reload finish once the add-on
# update entities reflect the reloaded store.
if (
command == STORE_RELOAD_ENDPOINT
and msg[ATTR_METHOD] == "post"
and (coordinator := hass.data.get(ADDONS_COORDINATOR))
):
await coordinator.async_refresh_after_store_reload()
connection.send_result(msg[WS_ID], data)
@@ -375,6 +375,56 @@ async def test_websocket_non_admin_user(
assert msg["error"]["message"] == "Unauthorized"
async def test_websocket_store_reload_refreshes_update_entities(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
aioclient_mock: AiohttpClientMocker,
supervisor_client: AsyncMock,
addons_list: AsyncMock,
) -> None:
"""Test add-on update entities refresh after a store reload via the API proxy."""
addons_list.return_value = [
replace(
addons_list.return_value[0],
update_available=False,
version_latest="2.0.0",
)
]
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
assert await async_setup_component(hass, DOMAIN, {"hassio": {}})
await hass.async_block_till_done()
assert hass.states.get("update.test_update").state == "off"
addons_list.return_value = [
replace(
addons_list.return_value[0],
update_available=True,
version_latest="2.0.1",
)
]
aioclient_mock.post(
"http://127.0.0.1/store/reload", json={"result": "ok", "data": {}}
)
websocket_client = await hass_ws_client(hass)
await websocket_client.send_json_auto_id(
{
WS_TYPE: WS_TYPE_API,
ATTR_ENDPOINT: "/store/reload",
ATTR_METHOD: "post",
}
)
msg = await websocket_client.receive_json()
assert msg["success"]
assert hass.states.get("update.test_update").state == "on"
supervisor_client.store.reload.assert_not_called()
async def test_update_addon(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,