0.5.3
This commit is contained in:
@@ -1,11 +1,146 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
# NOTE:
|
import logging
|
||||||
# Update entities will be implemented once installation/provider resolution is in place.
|
from dataclasses import dataclass
|
||||||
# This stub prevents platform load errors and keeps the integration stable in 0.3.0.
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.components.update import UpdateEntity, UpdateEntityFeature
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
|
|
||||||
|
from .core import DOMAIN, SIGNAL_UPDATED, BCSCore
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _pretty_repo_name(core: BCSCore, repo_id: str) -> str:
|
||||||
|
"""Return a human-friendly name for a repo update entity."""
|
||||||
|
try:
|
||||||
|
repo = core.get_repo(repo_id)
|
||||||
|
if repo and getattr(repo, "name", None):
|
||||||
|
name = str(repo.name).strip()
|
||||||
|
if name:
|
||||||
|
return name
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallbacks
|
||||||
|
if repo_id.startswith("index:"):
|
||||||
|
return f"BCS Index {repo_id.split(':', 1)[1]}"
|
||||||
|
if repo_id.startswith("custom:"):
|
||||||
|
return f"BCS Custom {repo_id.split(':', 1)[1]}"
|
||||||
|
return f"BCS {repo_id}"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class _RepoKey:
|
||||||
|
repo_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class BCSRepoUpdateEntity(UpdateEntity):
|
||||||
|
"""Update entity representing a BCS-managed repository."""
|
||||||
|
|
||||||
|
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||||
|
_attr_supported_features = UpdateEntityFeature.INSTALL
|
||||||
|
|
||||||
|
def __init__(self, core: BCSCore, repo_id: str) -> None:
|
||||||
|
self._core = core
|
||||||
|
self._repo_id = repo_id
|
||||||
|
self._in_progress = False
|
||||||
|
|
||||||
|
# Stable unique id (do NOT change)
|
||||||
|
self._attr_unique_id = f"{DOMAIN}:{repo_id}"
|
||||||
|
|
||||||
|
# Human-friendly name in UI
|
||||||
|
pretty = _pretty_repo_name(core, repo_id)
|
||||||
|
self._attr_name = pretty
|
||||||
|
|
||||||
|
# Title shown in the entity dialog
|
||||||
|
self._attr_title = pretty
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
repo = self._core.get_repo(self._repo_id)
|
||||||
|
installed = self._core.get_installed(self._repo_id)
|
||||||
|
return repo is not None and installed is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def in_progress(self) -> bool | None:
|
||||||
|
return self._in_progress
|
||||||
|
|
||||||
|
@property
|
||||||
|
def installed_version(self) -> str | None:
|
||||||
|
installed = self._core.get_installed(self._repo_id) or {}
|
||||||
|
v = installed.get("installed_version") or installed.get("ref")
|
||||||
|
return str(v) if v else None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def latest_version(self) -> str | None:
|
||||||
|
repo = self._core.get_repo(self._repo_id)
|
||||||
|
if not repo:
|
||||||
|
return None
|
||||||
|
v = getattr(repo, "latest_version", None)
|
||||||
|
return str(v) if v else None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def update_available(self) -> bool:
|
||||||
|
latest = self.latest_version
|
||||||
|
installed = self.installed_version
|
||||||
|
if not latest or not installed:
|
||||||
|
return False
|
||||||
|
return latest != installed
|
||||||
|
|
||||||
|
def version_is_newer(self, latest_version: str, installed_version: str) -> bool:
|
||||||
|
return latest_version != installed_version
|
||||||
|
|
||||||
|
@property
|
||||||
|
def release_url(self) -> str | None:
|
||||||
|
repo = self._core.get_repo(self._repo_id)
|
||||||
|
return getattr(repo, "url", None) if repo else None
|
||||||
|
|
||||||
|
async def async_install(self, version: str | None, backup: bool, **kwargs: Any) -> None:
|
||||||
|
if version is not None:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"BCS update entity requested specific version=%s (ignored)", version
|
||||||
|
)
|
||||||
|
|
||||||
|
self._in_progress = True
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self._core.update_repo(self._repo_id)
|
||||||
|
finally:
|
||||||
|
self._in_progress = False
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _sync_entities(
|
||||||
|
core: BCSCore,
|
||||||
|
existing: dict[str, BCSRepoUpdateEntity],
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Ensure there is one update entity per installed repo."""
|
||||||
|
installed_map = getattr(core, "_installed_cache", {}) or {}
|
||||||
|
new_entities: list[BCSRepoUpdateEntity] = []
|
||||||
|
|
||||||
|
for repo_id, data in installed_map.items():
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
continue
|
||||||
|
if repo_id in existing:
|
||||||
|
continue
|
||||||
|
|
||||||
|
ent = BCSRepoUpdateEntity(core, repo_id)
|
||||||
|
existing[repo_id] = ent
|
||||||
|
new_entities.append(ent)
|
||||||
|
|
||||||
|
if new_entities:
|
||||||
|
async_add_entities(new_entities)
|
||||||
|
|
||||||
|
for ent in existing.values():
|
||||||
|
ent.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
@@ -14,4 +149,18 @@ async def async_setup_platform(
|
|||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
discovery_info=None,
|
discovery_info=None,
|
||||||
):
|
):
|
||||||
|
"""Set up BCS update entities."""
|
||||||
|
core: BCSCore | None = hass.data.get(DOMAIN)
|
||||||
|
if not core:
|
||||||
|
_LOGGER.debug("BCS core not available, skipping update platform setup")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
entities: dict[str, BCSRepoUpdateEntity] = {}
|
||||||
|
|
||||||
|
_sync_entities(core, entities, async_add_entities)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _handle_update() -> None:
|
||||||
|
_sync_entities(core, entities, async_add_entities)
|
||||||
|
|
||||||
|
async_dispatcher_connect(hass, SIGNAL_UPDATED, _handle_update)
|
||||||
Reference in New Issue
Block a user