diff --git a/custom_components/bahmcloud_store/__init__.py b/custom_components/bahmcloud_store/__init__.py new file mode 100644 index 0000000..cff62ce --- /dev/null +++ b/custom_components/bahmcloud_store/__init__.py @@ -0,0 +1,144 @@ +from __future__ import annotations + +import logging +from datetime import timedelta + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.helpers.event import async_track_time_interval +from homeassistant.components import frontend +from homeassistant.components.http import HomeAssistantView + +from .store import BahmcloudStore, StoreConfig, StoreError + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = "bahmcloud_store" + +CONF_STORE_URL = "store_url" +CONF_AUTO_UPDATE = "auto_update" +CONF_CHECK_INTERVAL_MIN = "check_interval_minutes" + +DEFAULT_STORE_URL = "https://git.bahmcloud.de/bahmcloud/ha_store/raw/branch/main/store.json" +DEFAULT_AUTO_UPDATE = True +DEFAULT_CHECK_INTERVAL_MIN = 60 + + +async def async_setup(hass: HomeAssistant, config: dict) -> bool: + # Minimaler YAML-Config-Block (damit es ohne Config Flow sofort läuft) + # Du kannst das später auf Config Flow umbauen, wenn du willst. + cfg = config.get(DOMAIN, {}) + store_url = cfg.get(CONF_STORE_URL, DEFAULT_STORE_URL) + auto_update = cfg.get(CONF_AUTO_UPDATE, DEFAULT_AUTO_UPDATE) + interval_min = int(cfg.get(CONF_CHECK_INTERVAL_MIN, DEFAULT_CHECK_INTERVAL_MIN)) + + store = BahmcloudStore( + hass, + StoreConfig( + store_url=store_url, + auto_update=auto_update, + check_interval=timedelta(minutes=interval_min), + ), + ) + hass.data[DOMAIN] = store + + # Panel in der Seitenleiste registrieren (wie HACS-Icon) + frontend.async_register_built_in_panel( + hass, + component_name=DOMAIN, + sidebar_title="Bahmcloud Store", + sidebar_icon="mdi:store", + frontend_url_path="bahmcloud-store", + config={}, + require_admin=True, + ) + + # HTTP Views (UI + API) + hass.http.register_view(BahmcloudStoreUI(store)) + hass.http.register_view(BahmcloudStoreAPI(store)) + + # Services + async def svc_install(call: ServiceCall) -> None: + package_id = call.data["package_id"] + await store.install(package_id) + + async def svc_update(call: ServiceCall) -> None: + package_id = call.data["package_id"] + await store.update(package_id) + + async def svc_update_all(call: ServiceCall) -> None: + await store.update_all() + + hass.services.async_register(DOMAIN, "install", svc_install) + hass.services.async_register(DOMAIN, "update", svc_update) + hass.services.async_register(DOMAIN, "update_all", svc_update_all) + + # Initial load + try: + await store.refresh() + except StoreError as e: + _LOGGER.error("Store refresh failed: %s", e) + + # Periodische Checks + optional Auto-Update + async def periodic(_now) -> None: + try: + await store.refresh() + if store.config.auto_update: + await store.update_all() + except StoreError as e: + _LOGGER.warning("Periodic store check failed: %s", e) + + async_track_time_interval(hass, periodic, timedelta(minutes=interval_min)) + + return True + + +class BahmcloudStoreUI(HomeAssistantView): + """Serves the minimal UI page.""" + requires_auth = True + name = "bahmcloud_store_ui" + url = "/api/bahmcloud_store/ui" + + def __init__(self, store: BahmcloudStore) -> None: + self._store = store + + async def get(self, request): + # serve index.html from package folder + content = self._store.load_panel_file("index.html") + return self.json({"html": content}) + + +class BahmcloudStoreAPI(HomeAssistantView): + """Simple JSON API for the panel JS.""" + requires_auth = True + name = "bahmcloud_store_api" + url = "/api/bahmcloud_store" + + def __init__(self, store: BahmcloudStore) -> None: + self._store = store + + async def get(self, request): + # /api/bahmcloud_store?op=list + op = request.query.get("op", "list") + if op == "list": + await self._store.refresh() + return self.json(self._store.as_dict()) + return self.json({"error": "unknown op"}, status_code=400) + + async def post(self, request): + data = await request.json() + op = data.get("op") + if op in ("install", "update"): + package_id = data.get("package_id") + if not package_id: + return self.json({"error": "package_id missing"}, status_code=400) + if op == "install": + await self._store.install(package_id) + else: + await self._store.update(package_id) + return self.json({"ok": True}) + if op == "update_all": + await self._store.update_all() + return self.json({"ok": True}) + + return self.json({"error": "unknown op"}, status_code=400)