diff --git a/addon/config.yaml b/addon/config.yaml index 4ff9dc1..3b77b84 100644 --- a/addon/config.yaml +++ b/addon/config.yaml @@ -1,5 +1,5 @@ name: SillyHome Next -version: "1.7.2" +version: "1.7.3" slug: sillyhome_next description: Lernt automatisch aus deinem Verhalten und steuert freigegebene Aktoren url: http://192.168.6.31:3000/pino/sillyhome-next diff --git a/app/main.py b/app/main.py index 600f913..3adcc22 100644 --- a/app/main.py +++ b/app/main.py @@ -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.7.2", + version="1.7.3", lifespan=lifespan, ) app.state.settings = load_settings() diff --git a/app/static/index.html b/app/static/index.html index 31d9a1a..a740aa2 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -281,6 +281,7 @@ let discoveryLoadPromise = null; let overviewLoadPromise = null; let systemLoadPromise = null; let currentSensorWeightGroups = []; +let latestSimulationResults = new Map(); let visibleActuatorLimit = 24; const ACTUATOR_RESULT_LIMIT = 50; const STATUS_TIMEOUT_MS = 2000; @@ -1255,7 +1256,7 @@ async function showActuator(actuatorId, evaluationMessage = "") { const simulationControls = weightedCandidates.length ? `
Aktor-Simulation -

Teste Sensorzustände und Gewichtungen, ohne Home Assistant zu schalten.

+

Teste Sensorzustände und Gewichtungen, ohne Home Assistant zu schalten. Danach kannst du die beste Gewichtung übernehmen oder direkt in den Dry-run wechseln.

${weightedCandidates.map(candidate => { const effective = Math.round((candidate.effective_weight ?? 1) * 100); @@ -1677,6 +1678,7 @@ async function simulateActuator(actuatorId) { max_results: 6, }), }); + latestSimulationResults.set(actuatorId, results); box.innerHTML = results.length ? results.map((result, index) => { const prediction = result.prediction; const factors = result.decision_factors || []; @@ -1691,6 +1693,10 @@ async function simulateActuator(actuatorId) {

Gewichtung: ${Object.entries(result.sensor_weights || {}).map(([entity, weight]) => `${escapeHtml(entity)}=${Math.round(weight * 100)} %`).join(", ") || "Standard"}

${result.blockers?.length ? `

${result.blockers.map(escapeHtml).join(" ")}

` : "

Würde nach Sicherheitsprüfung schalten.

"} ${factors.length ? `` : ""} +
+ + +
`; }).join("") : "

Keine Simulationsergebnisse.

"; @@ -1699,6 +1705,41 @@ async function simulateActuator(actuatorId) { } } +async function applySimulationWeights(actuatorId, scenarioId, startDryRun) { + const result = (latestSimulationResults.get(actuatorId) || []) + .find(item => item.scenario_id === scenarioId); + if (!result) { + alert("Simulationsergebnis ist nicht mehr verfügbar. Bitte neu simulieren."); + return; + } + try { + await api(`v1/actuators/${encodeURIComponent(actuatorId)}/weights`, { + method: "POST", + body: JSON.stringify({ + sensor_weights: result.sensor_weights || {}, + sensor_weight_groups: currentSensorWeightGroups, + note: `Aus Simulation ${scenarioId} übernommen`, + }), + }); + if (startDryRun) { + await api(`v1/actuators/${encodeURIComponent(actuatorId)}/dry-run`, { + method: "POST", + body: JSON.stringify({enabled: true}), + }); + } + invalidateDashboardCache(); + await loadConfiguredActuators(); + await showActuator( + actuatorId, + startDryRun + ? "Simulation übernommen und Dry-run gestartet." + : "Simulation übernommen.", + ); + } catch (error) { + alert(error.message); + } +} + async function saveManualAssignment(actuatorId) { const numericEntityId = document.getElementById("manual-numeric-select").value || null; const selectedContextIds = Array.from( diff --git a/pyproject.toml b/pyproject.toml index 94a14aa..3a27700 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "sillyhome-next" -version = "1.7.2" +version = "1.7.3" description = "Lokales Smart-Home-Intelligenzsystem für Home Assistant" requires-python = ">=3.11" dependencies = [