Fix ingress logging and dashboard cache navigation
Some checks failed
quality / test (3.11) (push) Has been cancelled
quality / test (3.13) (push) Has been cancelled

This commit is contained in:
2026-06-18 00:30:12 +02:00
parent 1d176cce45
commit 6323b93f23
9 changed files with 147 additions and 16 deletions

View File

@@ -154,7 +154,6 @@
<select id="section-jump" onchange="showView(this.value)">
<option value="status-section">Startseite / System</option>
<option value="observed">Lernen</option>
<option value="detail">Details</option>
<option value="choose">Discovery & Einrichtung</option>
<option value="settings">Einstellungen</option>
<option value="guide">Ablauf</option>
@@ -309,7 +308,12 @@ let manualContextState = {options: [], selected: new Set()};
let cachedActuators = null;
let cachedEntities = null;
let cachedDiscovery = null;
let cachedSystemOverview = null;
let cachedDashboardOverview = null;
let cachedDetailHtml = new Map();
let discoveryLoadPromise = null;
let overviewLoadPromise = null;
let systemLoadPromise = null;
let currentSensorWeightGroups = [];
let visibleActuatorLimit = 24;
const ACTUATOR_RESULT_LIMIT = 50;
@@ -465,14 +469,29 @@ function jumpToSection(target) {
}
function showView(viewId) {
if (viewId === "detail" && !currentActuatorId) {
viewId = "observed";
}
for (const section of document.querySelectorAll(".app-view")) {
section.classList.toggle("active", section.id === viewId);
}
localStorage.setItem("sillyhome.ui.view", viewId);
if (viewId === "status-section") {
void loadSystemOverview();
if (cachedSystemOverview) {
renderDashboardStatus(cachedSystemOverview);
refreshSystemOverviewInBackground();
} else {
void loadSystemOverview();
}
} else if (viewId === "observed") {
void loadOverview();
if (cachedActuators) {
renderConfiguredActuators();
refreshOverviewInBackground();
} else {
void loadOverview();
}
} else if (viewId === "choose") {
if (cachedDiscovery) renderActuatorDiscovery();
} else if (viewId === "settings") {
syncSettingsView();
}
@@ -484,8 +503,10 @@ function setLanguage(language) {
localStorage.setItem("sillyhome.ui.language", uiLang);
syncSettingsView();
if (cachedActuators) renderConfiguredActuators();
void loadSystemOverview();
if (currentActuatorId) void showActuator(currentActuatorId);
if (cachedSystemOverview) renderDashboardStatus(cachedSystemOverview);
if (currentActuatorId && cachedDetailHtml.has(currentActuatorId)) {
document.getElementById("actuator-detail").innerHTML = cachedDetailHtml.get(currentActuatorId);
}
}
function syncSettingsView() {
@@ -514,6 +535,9 @@ function invalidateDashboardCache() {
cachedActuators = null;
cachedEntities = null;
cachedDiscovery = null;
cachedDashboardOverview = null;
cachedSystemOverview = null;
cachedDetailHtml.clear();
}
async function api(path, options = {}) {
@@ -654,13 +678,24 @@ function optionGroups(entities, selectedIds = new Set()) {
}
async function loadOverview() {
if (overviewLoadPromise) return overviewLoadPromise;
overviewLoadPromise = doLoadOverview().finally(() => {
overviewLoadPromise = null;
});
return overviewLoadPromise;
}
async function doLoadOverview() {
const startedAt = performance.now();
const budget = document.getElementById("load-budget");
if (budget) budget.textContent = "Startdaten laden ...";
document.getElementById("configured-actuators").innerHTML = "<p class='muted'>Beobachtete Geräte werden geladen ...</p>";
if (!cachedActuators) {
document.getElementById("configured-actuators").innerHTML = "<p class='muted'>Beobachtete Geräte werden geladen ...</p>";
}
try {
const dashboard = await api("v1/actuators/dashboard/start");
dashboard._load_elapsed_ms = Math.round(performance.now() - startedAt);
cachedDashboardOverview = dashboard;
cachedActuators = dashboard.actuators || [];
cachedEntities = [];
renderDashboardStatus(dashboard);
@@ -685,12 +720,21 @@ async function loadOverview() {
}
async function loadSystemOverview() {
if (systemLoadPromise) return systemLoadPromise;
systemLoadPromise = doLoadSystemOverview().finally(() => {
systemLoadPromise = null;
});
return systemLoadPromise;
}
async function doLoadSystemOverview() {
const startedAt = performance.now();
const budget = document.getElementById("load-budget");
if (budget) budget.textContent = "Systemübersicht lädt ...";
try {
const dashboard = await api("v1/actuators/dashboard/system");
dashboard._load_elapsed_ms = Math.round(performance.now() - startedAt);
cachedSystemOverview = dashboard;
cachedActuators = dashboard.actuators || cachedActuators;
renderDashboardStatus(dashboard);
if (budget) {
@@ -706,6 +750,22 @@ async function loadSystemOverview() {
}
}
function refreshOverviewInBackground() {
if (!overviewLoadPromise) {
overviewLoadPromise = doLoadOverview().finally(() => {
overviewLoadPromise = null;
});
}
}
function refreshSystemOverviewInBackground() {
if (!systemLoadPromise) {
systemLoadPromise = doLoadSystemOverview().finally(() => {
systemLoadPromise = null;
});
}
}
function scheduleDashboardExtras() {
const run = () => {
void loadDashboardExtras();
@@ -1131,10 +1191,20 @@ async function showActuator(actuatorId, evaluationMessage = "") {
for (const section of document.querySelectorAll(".app-view")) {
section.classList.toggle("active", section.id === "detail");
}
document.getElementById("section-jump").value = "detail";
document.getElementById("section-jump").value = "observed";
localStorage.setItem("sillyhome.ui.view", "detail");
const box = document.getElementById("actuator-detail");
renderActuatorDetailShell(actuatorId);
if (!evaluationMessage && cachedDetailHtml.has(actuatorId)) {
box.innerHTML = cachedDetailHtml.get(actuatorId);
document.getElementById("detail").scrollIntoView({behavior: "smooth", block: "start"});
renderConfiguredActuators();
return;
}
if (cachedDetailHtml.has(actuatorId)) {
box.innerHTML = cachedDetailHtml.get(actuatorId);
} else {
renderActuatorDetailShell(actuatorId);
}
try {
const record = await api(`v1/actuators/${encodeURIComponent(actuatorId)}/detail`);
contextOptions = [];
@@ -1403,13 +1473,13 @@ async function showActuator(actuatorId, evaluationMessage = "") {
<button class="secondary compact" onclick="setRelatedAutomation('${escapeHtml(record.actuator_entity_id)}', '${escapeHtml(automation.entity_id)}', ${automation.enabled ? "false" : "true"})">${automation.enabled ? "Pausieren" : "Fortsetzen"}</button>
</li>`).join("")}</ul>`
: "<p class='muted'>Keine eindeutig passende HA-Automation gefunden.</p>";
box.innerHTML = `
const detailHtml = `
<div class="detail-header">
<div>
<h3>${escapeHtml(record.actuator_entity_id)}</h3>
<p class="muted">Alle wichtigen Aktionen für dieses Gerät.</p>
</div>
<button class="secondary compact" onclick="loadOverview()">Alles aktualisieren</button>
<button class="secondary compact" onclick="showActuator('${escapeHtml(record.actuator_entity_id)}', 'Aktualisiert.')">Details aktualisieren</button>
</div>
<div class="grid-two">
<div>
@@ -1461,7 +1531,10 @@ async function showActuator(actuatorId, evaluationMessage = "") {
${currentContextControls}
${manualAssignment}
`;
box.innerHTML = detailHtml;
cachedDetailHtml.set(actuatorId, detailHtml);
document.getElementById("detail").scrollIntoView({behavior: "smooth", block: "start"});
renderConfiguredActuators();
} catch (error) {
box.textContent = error.message;
}
@@ -1768,8 +1841,11 @@ async function startDashboard() {
document.getElementById("configured-actuators").innerHTML = "<div class='empty-state'>Öffne „Lernen“, um Geräte zu laden.</div>";
document.getElementById("actuator-detail").innerHTML = "<div class='empty-state'>Wähle später ein Gerät aus der Übersicht.</div>";
syncSettingsView();
document.getElementById("section-jump").value = "status-section";
showView("status-section");
const initialView = localStorage.getItem("sillyhome.ui.view") === "detail"
? "observed"
: (localStorage.getItem("sillyhome.ui.view") || "status-section");
document.getElementById("section-jump").value = initialView;
showView(initialView);
await new Promise(resolve => requestAnimationFrame(resolve));
setTimeout(() => void loadStatus(), 100);
}