Add simulation apply workflow
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.7.2",
|
||||
version="1.7.3",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
app.state.settings = load_settings()
|
||||
|
||||
@@ -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 ? `
|
||||
<details class="manual-context" open>
|
||||
<summary>Aktor-Simulation</summary>
|
||||
<p class="muted">Teste Sensorzustände und Gewichtungen, ohne Home Assistant zu schalten.</p>
|
||||
<p class="muted">Teste Sensorzustände und Gewichtungen, ohne Home Assistant zu schalten. Danach kannst du die beste Gewichtung übernehmen oder direkt in den Dry-run wechseln.</p>
|
||||
<div class="card-list">
|
||||
${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) {
|
||||
<p class="muted">Gewichtung: ${Object.entries(result.sensor_weights || {}).map(([entity, weight]) => `${escapeHtml(entity)}=${Math.round(weight * 100)} %`).join(", ") || "Standard"}</p>
|
||||
${result.blockers?.length ? `<p class="warn">${result.blockers.map(escapeHtml).join(" ")}</p>` : "<p class='ok'>Würde nach Sicherheitsprüfung schalten.</p>"}
|
||||
${factors.length ? `<ul>${factors.slice(0, 4).map(factor => `<li>${escapeHtml(factor.label)}: ${Math.round((factor.contribution || 0) * 100)} % Beitrag</li>`).join("")}</ul>` : ""}
|
||||
<div class="actions">
|
||||
<button class="secondary compact" onclick="applySimulationWeights('${escapeHtml(actuatorId)}', '${escapeHtml(result.scenario_id)}', false)">Gewichtung übernehmen</button>
|
||||
<button class="compact" onclick="applySimulationWeights('${escapeHtml(actuatorId)}', '${escapeHtml(result.scenario_id)}', true)">Übernehmen + Dry-run starten</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join("") : "<p class='muted'>Keine Simulationsergebnisse.</p>";
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user