Fix ingress logging and dashboard cache navigation
This commit is contained in:
@@ -117,7 +117,7 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
||||
app = FastAPI(
|
||||
title="SillyHome Next API",
|
||||
description="Lokales Smart-Home-Intelligenzsystem für Home Assistant.",
|
||||
version="1.5.3",
|
||||
version="1.5.4",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
app.state.settings = load_settings()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user