Updated apps

This commit is contained in:
2026-07-20 22:52:35 -04:00
parent 28a8cb98f6
commit a0c3271743
1164 changed files with 94781 additions and 6892 deletions
+78 -4
View File
@@ -29,8 +29,9 @@ from .const import (
DOMAIN,
OPT_AUTO_UPDATE,
)
from .coordinator import ServerVersionCoordinator
from .coordinator import ServerVersionCoordinator, ServerVersionInfo
from .embedded_server import _installed_dist_version
from .embedded_setup import _async_update_held_by_component
if TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
@@ -46,6 +47,19 @@ _RELEASES_URL = (
)
_RELEASE_NOTES_TIMEOUT_SECONDS = 15
# Prepended to the release notes while the automatic server update is HELD on a
# newer custom component (embedded_setup's auto-update gate). ``ha-alert`` renders
# as a prominent banner in Home Assistant's markdown; ``{shipped}`` is the
# component version the release ships, ``{running}`` the one currently installed.
_COMPONENT_HOLD_WARNING = (
'<ha-alert alert-type="warning">\n'
"This release also updates the HA-MCP Custom Component (to {shipped}; you "
"are running {running}). Update the component in HACS first — installing "
"this server update now runs a server build the HACS component has never "
"been tested with.\n"
"</ha-alert>"
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
@@ -120,17 +134,77 @@ class ServerUpdateEntity(CoordinatorEntity[ServerVersionCoordinator], UpdateEnti
return features
async def async_release_notes(self) -> str | None:
"""Concatenate GitHub release bodies between installed and latest.
"""Return the GitHub release notes, with a component-update warning
prepended when the pending update is held on a component update.
When the newer server release also ships a newer custom component than
the one running, the auto-update gate HOLDS the install (see
embedded_setup._async_update_held_by_component), so the dialog leads
with a prominent warning to update the component in HACS first. That
warning must survive even a failed or empty notes fetch — surfacing it
is the whole point of opening a held update's dialog — so a held update
returns the warning alone rather than None. When not held, behaviour is
unchanged.
Advisory-only (same reasoning as embedded_setup's
_async_check_component_compat): a GitHub fetch failure, rate limit, or
unexpected payload shape must degrade to None - the UI then falls back
to :attr:`release_url` - rather than break the update dialog.
unexpected payload shape degrades to the :attr:`release_url` fallback
rather than breaking the update dialog.
"""
data = self.coordinator.data
if data is None or data.installed is None or data.latest is None:
return None
# Both probes are advisory network calls that contain their own
# failures and timeouts; run them concurrently so the dialog waits for
# the slower of the two, not their sum — a blocked/slow manifest host
# must not stall the ordinary notes fetch (review finding).
warning, notes = await asyncio.gather(
self._async_component_hold_warning(data),
self._async_fetch_release_notes(data),
)
if warning is None:
# Not held: exactly the pre-existing behaviour (the notes, or None
# on any failure/empty — the UI then falls back to release_url).
return notes
# Held: the warning must always surface, even when the notes fetch
# failed or returned nothing — it must never vanish with the notes.
if notes is None:
return warning
return f"{warning}\n\n{notes}"
async def _async_component_hold_warning(
self, data: ServerVersionInfo
) -> str | None:
"""Return the markdown component-hold warning, or None when not held.
Reuses the auto-update gate's own held-check so this dialog and the
Repairs hold agree on when the component is behind. Fully advisory: any
failure — including an unexpected error escaping the gate — degrades to
None so the hold warning can never break the release-notes dialog.
"""
try:
held = await _async_update_held_by_component(self.hass, data)
except Exception:
# The gate contains all its expected failures internally, so an
# exception escaping it is a bug — logged visibly per the repo's
# convention (review finding), still degrading to plain notes.
_LOGGER.warning(
"HA-MCP release-notes component-hold check failed", exc_info=True
)
return None
if held is None:
return None
shipped, running = held
return _COMPONENT_HOLD_WARNING.format(shipped=shipped, running=running)
async def _async_fetch_release_notes(self, data: ServerVersionInfo) -> str | None:
"""Concatenate GitHub release bodies between installed and latest.
Advisory-only: a GitHub fetch failure, rate limit, or unexpected payload
shape degrades to None so the dialog falls back to :attr:`release_url`.
"""
try:
installed = AwesomeVersion(data.installed)
latest = AwesomeVersion(data.latest)