function serviceTone(status) { if (status === "offline") return "offline"; if (status === "degraded") return "warning"; return "online"; } function healthLabel(status) { if (status === "offline") return "Offline"; if (status === "degraded") return "Degraded"; return "Healthy"; } function sourceLabel(source) { if (source === "home_assistant") return "Core Automation Hub"; if (source === "uptime_kuma") return "External availability and latency surface."; if (source === "docker") return "Container runtime state without external monitor data."; return "Service state from aggregator."; } function formatTimestamp(value) { if (!value) return "n/a"; const date = new Date(value); return new Intl.DateTimeFormat("de-DE", { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false, }).format(date); } export function renderServices(state) { const { services, overview } = state.data; const dockerPill = document.getElementById("docker-summary-pill"); dockerPill.className = `status-pill ${services.summary.docker.source_status === "online" ? "online" : "offline"}`; dockerPill.textContent = services.summary.docker.source_status === "online" ? "Online" : "Offline"; document.getElementById("docker-running").textContent = String(services.summary.docker.running); document.getElementById("docker-stopped").textContent = String(services.summary.docker.stopped); document.getElementById("docker-unhealthy").textContent = String(services.summary.docker.unhealthy); const kumaPill = document.getElementById("kuma-summary-pill"); kumaPill.className = `status-pill ${services.summary.uptime_kuma.source_status === "online" ? "online" : "offline"}`; kumaPill.textContent = services.summary.uptime_kuma.source_status === "online" ? "Synced" : "Offline"; document.getElementById("kuma-up").textContent = String(services.summary.uptime_kuma.monitors_up); document.getElementById("kuma-down").textContent = String(services.summary.uptime_kuma.monitors_down); document.getElementById("kuma-paused").textContent = String(services.summary.uptime_kuma.monitors_paused); const ha = services.services.find((service) => service.id === "homeassistant"); if (ha) { const haPill = document.getElementById("service-ha-pill"); const tone = serviceTone(ha.status); haPill.className = `status-pill ${tone}`; haPill.textContent = ha.status === "online" ? "Reachable" : ha.status === "degraded" ? "Degraded" : "Offline"; document.getElementById("service-ha-version").textContent = overview.home_assistant.version ?? "unknown"; document.getElementById("service-ha-version").className = "info"; document.getElementById("service-ha-latency").textContent = ha.latency_ms != null ? `${ha.latency_ms} MS` : "N/A"; document.getElementById("service-ha-latency").className = tone === "offline" ? "offline" : tone === "warning" ? "warning" : "online"; document.getElementById("service-ha-last-check").textContent = formatTimestamp(ha.last_checked); } const dynamicServices = services.services.filter((service) => service.id !== "homeassistant").slice(0, 3); const existingFallbacks = [ document.getElementById("service-card-fallback-1"), document.getElementById("service-card-fallback-2"), document.getElementById("service-card-fallback-3"), ]; dynamicServices.forEach((service, index) => { const node = existingFallbacks[index]; if (!node) return; node.style.display = ""; const tone = serviceTone(service.status); const pillClass = tone === "warning" ? "warning" : tone === "offline" ? "offline" : "online"; node.querySelector(".card-label").textContent = service.name; node.querySelector(".status-pill").className = `status-pill ${pillClass}`; node.querySelector(".status-pill").textContent = healthLabel(service.status); node.querySelector(".card-title").textContent = service.name === "Immich" ? "Photo Pipeline" : service.name === "Gitea" ? "Git Platform" : `${service.name} Service`; node.querySelector(".card-copy").textContent = sourceLabel(service.source); const rows = node.querySelectorAll(".service-meta-row strong"); rows[0].textContent = service.latency_ms != null ? `${service.latency_ms} MS` : "N/A"; rows[0].className = tone === "offline" ? "offline" : tone === "warning" ? "warning" : "online"; rows[1].textContent = String(service.docker_state).toUpperCase(); rows[1].className = service.docker_state === "stopped" ? "offline" : service.docker_state === "unhealthy" ? "warning" : "online"; }); for (let index = dynamicServices.length; index < existingFallbacks.length; index += 1) { existingFallbacks[index].style.display = "none"; } }