This commit is contained in:
2026-01-20 05:45:02 +00:00
parent 95dd8b9dc2
commit 4c2a104af7

View File

@@ -6,48 +6,53 @@ from datetime import timedelta
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.components.panel_custom import async_register_panel from homeassistant.components.panel_custom import async_register_panel
from homeassistant.helpers.event import async_track_time_interval, async_call_later
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
from homeassistant.helpers.event import async_call_later, async_track_time_interval
from .core import BCSCore, BCSConfig, BCSError from .const import CONF_GITHUB_TOKEN, DEFAULT_STORE_URL, DOMAIN
from .config_flow import CONF_GITHUB_TOKEN from .core import BCSError, BCSConfig, BCSCore
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DOMAIN = "bahmcloud_store"
# Fixed store index URL (not configurable by users)
DEFAULT_STORE_URL = "https://git.bahmcloud.de/bahmcloud/ha_store/raw/branch/main/store.yaml"
PLATFORMS: list[str] = ["update"] PLATFORMS: list[str] = ["update"]
DATA_ENTRY = f"{DOMAIN}_entry_runtime"
async def async_setup(hass: HomeAssistant, config: dict) -> bool: async def async_setup(hass: HomeAssistant, config: dict) -> bool:
"""Set up Bahmcloud Store. """Set up Bahmcloud Store.
YAML configuration is intentionally not supported. We intentionally do NOT support YAML configuration.
Setup must happen via the UI (Config Flow). This method is kept so we can log a helpful message if someone tries.
""" """
if DOMAIN in (config or {}): if DOMAIN in (config or {}):
_LOGGER.warning( _LOGGER.warning(
"BCS does not support configuration.yaml setup. " "BCS YAML configuration is no longer supported. "
"Please remove 'bahmcloud_store:' from configuration.yaml and set up the integration via Settings -> Devices & Services." "Please remove 'bahmcloud_store:' from configuration.yaml and set up the integration via the UI."
) )
return True return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Bahmcloud Store from a config entry.""" """Set up Bahmcloud Store from a config entry (UI setup)."""
token = (entry.options.get(CONF_GITHUB_TOKEN) or "").strip() or None # Only one instance.
hass.data.setdefault(DOMAIN, {})
core = BCSCore(hass, BCSConfig(store_url=DEFAULT_STORE_URL, github_token=token)) github_token = (entry.options.get(CONF_GITHUB_TOKEN) or "").strip() or None
hass.data[DOMAIN] = core
core = BCSCore(
hass,
BCSConfig(
store_url=DEFAULT_STORE_URL,
github_token=github_token,
),
)
hass.data[DOMAIN][entry.entry_id] = core
# Keep a convenient shortcut for platforms that previously used hass.data[DOMAIN] directly.
hass.data[DOMAIN]["_core"] = core
await core.async_initialize() await core.async_initialize()
# Register HTTP views + static assets # HTTP views + panel (registered once per entry; we only allow one entry).
from .views import ( from .views import (
StaticAssetsView, StaticAssetsView,
BCSApiView, BCSApiView,
@@ -78,7 +83,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.http.register_view(BCSRestoreView(core)) hass.http.register_view(BCSRestoreView(core))
hass.http.register_view(BCSRestartView(core)) hass.http.register_view(BCSRestartView(core))
# Sidebar panel
await async_register_panel( await async_register_panel(
hass, hass,
frontend_url_path="bahmcloud-store", frontend_url_path="bahmcloud-store",
@@ -91,16 +95,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
config={}, config={},
) )
# Forward platform setup (Update entities)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async def _do_startup_refresh(_now=None) -> None: async def _do_startup_refresh(_now=None) -> None:
try: try:
await core.full_refresh(source="startup") await core.full_refresh(source="startup")
except BCSError as e: except BCSError as e:
_LOGGER.error("Initial refresh failed: %s", e) _LOGGER.error("Initial refresh failed: %s", e)
# Do not block HA startup: schedule refresh after HA started. # Do not block startup; refresh after HA is up.
def _on_ha_started(_event) -> None: def _on_ha_started(_event) -> None:
async_call_later(hass, 30, _do_startup_refresh) async_call_later(hass, 30, _do_startup_refresh)
@@ -115,30 +116,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
_LOGGER.exception("Unexpected error during periodic refresh: %s", e) _LOGGER.exception("Unexpected error during periodic refresh: %s", e)
interval_seconds = int(getattr(core, "refresh_seconds", 300) or 300) interval_seconds = int(getattr(core, "refresh_seconds", 300) or 300)
unsub = async_track_time_interval(hass, periodic, timedelta(seconds=interval_seconds)) async_track_time_interval(hass, periodic, timedelta(seconds=interval_seconds))
# Store unload callbacks safely await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
runtimes = hass.data.setdefault(DATA_ENTRY, {})
runtimes[entry.entry_id] = {"unsub_interval": unsub}
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
try:
runtimes = hass.data.get(DATA_ENTRY, {}) or {}
rt = runtimes.pop(entry.entry_id, {}) if isinstance(runtimes, dict) else {}
unsub = rt.get("unsub_interval")
if callable(unsub):
unsub()
except Exception:
pass
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok: if unload_ok:
hass.data.pop(DOMAIN, None) try:
hass.data.get(DOMAIN, {}).pop(entry.entry_id, None)
except Exception:
pass
return unload_ok return unload_ok
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
await hass.config_entries.async_reload(entry.entry_id)