Compare commits

...

1 Commits

Author SHA1 Message Date
da51ac2063 UI-001: simplify addon setup and explain ingress workflow
Some checks failed
quality / test (3.11) (pull_request) Has been cancelled
quality / test (3.13) (pull_request) Has been cancelled
2026-06-14 11:02:38 +02:00
7 changed files with 77 additions and 47 deletions

View File

@@ -1,5 +1,12 @@
# Changelog
## 0.5.1 - 2026-06-14
- Technische Modell-, Intervall- und Sicherheitsparameter aus der normalen
Home-Assistant-Add-on-Konfiguration entfernt; sichere Standardwerte bleiben aktiv
- Ingress um einen klaren Ablauf mit Aktorauswahl, Beobachtungsphase und späterer
Ausführungsfreigabe ergänzt
- Bedienelemente und Diagnosen in verständlicher Alltagssprache erklärt
## 0.5.0 - 2026-06-14
- Ingress auf reine Aktorauswahl, automatischen Lernstatus und Vorhersagen reduziert
- Automatische Kontextzuordnung ohne Sensor-Overrides oder Review-Blockade

View File

@@ -1,5 +1,5 @@
name: SillyHome Next
version: "0.5.0"
version: "0.5.1"
slug: sillyhome_next
description: Lernt automatisch aus deinem Verhalten und steuert freigegebene Aktoren
url: http://192.168.6.31:3000/pino/sillyhome-next
@@ -16,28 +16,6 @@ panel_admin: true
homeassistant_api: true
hassio_api: false
auth_api: false
options:
history_days: 14
min_training_points: 24
retrain_stale_hours: 24
reconcile_interval_seconds: 900
min_behavior_actions: 3
prediction_confidence: 0.82
prediction_window_minutes: 30
prediction_interval_seconds: 60
execution_cooldown_seconds: 900
timezone: Europe/Berlin
schema:
history_days: "int(1,31)"
min_training_points: "int(2,10000)"
retrain_stale_hours: "int(1,720)"
reconcile_interval_seconds: "int(60,86400)"
min_behavior_actions: "int(2,100)"
prediction_confidence: "float(0.5,0.99)"
prediction_window_minutes: "int(5,120)"
prediction_interval_seconds: "int(30,3600)"
execution_cooldown_seconds: "int(60,86400)"
timezone: "str"
map:
- type: addon_config
read_only: false

View File

@@ -77,7 +77,7 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
app = FastAPI(
title="SillyHome Next API",
description="Lokales Smart-Home-Intelligenzsystem für Home Assistant.",
version="0.5.0",
version="0.5.1",
lifespan=lifespan,
)
app.state.settings = load_settings()

View File

@@ -13,6 +13,10 @@
main { display: grid; grid-template-columns: repeat(auto-fit,minmax(320px,1fr)); gap: 14px; padding: 14px; }
section { background: #18212b; border: 1px solid #2d3a47; border-radius: 12px; padding: 16px; }
.wide { grid-column: 1 / -1; }
.steps { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:12px; }
.step { background:#111a23; border:1px solid #31404d; border-radius:10px; padding:14px; }
.step-number { display:inline-grid; place-items:center; width:28px; height:28px; border-radius:50%; background:#23715b; font-weight:700; margin-bottom:8px; }
.step p { margin:5px 0; }
.ok { color: #66dfa9; }
.warn { color: #f3c969; }
.bad { color: #ff8f8f; }
@@ -34,34 +38,61 @@
<body>
<header>
<h1>SillyHome Next</h1>
<p>Du wählst nur die Aktoren. SillyHome findet Kontext, lernt Gewohnheiten und trifft Vorhersagen im Shadow-Modus.</p>
<p class="notice">Geschaltet wird erst nach deiner ausdrücklichen Freigabe pro Aktor.</p>
<p>Hier wählst du nur Geräte aus, deren Bedienung SillyHome lernen soll. Sensoren, Zusammenhänge und Modelle werden automatisch verwaltet.</p>
<p class="notice">Sicherer Start: Zuerst wird nur beobachtet und vorhergesagt. Ohne deine spätere Freigabe wird nichts geschaltet.</p>
</header>
<main>
<section class="wide">
<h2>So gehst du vor</h2>
<div class="steps">
<div class="step">
<span class="step-number">1</span>
<h3>Aktor auswählen</h3>
<p><strong>Wo?</strong> Unten im Feld „Gerät auswählen“.</p>
<p><strong>Was passiert?</strong> SillyHome ordnet Raum, Sensoren, Zustände und vorhandene Historie automatisch zu.</p>
</div>
<div class="step">
<span class="step-number">2</span>
<h3>Wie gewohnt bedienen</h3>
<p><strong>Wo?</strong> Weiterhin in Home Assistant, an Schaltern oder über deine bisherigen Bedienwege.</p>
<p><strong>Was passiert?</strong> SillyHome lernt deine Handlungen und zeigt Vorhersagen an, schaltet aber noch nicht selbst.</p>
</div>
<div class="step">
<span class="step-number">3</span>
<h3>Später freigeben</h3>
<p><strong>Wo?</strong> In den Details des ausgewählten Geräts, sobald genug Verhalten gelernt wurde.</p>
<p><strong>Was passiert?</strong> Erst dann darf SillyHome passende Vorhersagen automatisch ausführen. Die Freigabe kann jederzeit gestoppt werden.</p>
</div>
</div>
</section>
<section>
<h2>Systemstatus</h2>
<p class="muted">Zeigt, ob Verbindung, Lernsystem und automatische Prüfungen funktionieren. Hier musst du normalerweise nichts einstellen.</p>
<div id="status">Prüfung läuft ...</div>
<div class="chips" id="status-chips"></div>
<button class="secondary" onclick="loadOverview()">Status aktualisieren</button>
</section>
<section>
<h2>Aktor freigeben</h2>
<p class="muted">Nach der Auswahl analysiert SillyHome automatisch passende Sensoren, Zustände und Historie.</p>
<label for="actuator-select">Home-Assistant-Aktor</label>
<h2>1. Gerät zum Lernen auswählen</h2>
<p class="muted">Wähle eine Lampe, einen Rollladen oder einen anderen unterstützten Aktor. Du wählst keine Sensoren und erstellst keine Regeln.</p>
<label for="actuator-select">Gerät aus Home Assistant</label>
<select id="actuator-select"></select>
<button onclick="configureActuator()">Auswählen und Lernen starten</button>
<button onclick="configureActuator()">Gerät hinzufügen und Beobachtung starten</button>
<p id="actuator-config-result" class="muted">Noch kein Aktor ausgewählt.</p>
</section>
<section class="wide">
<h2>Ausgewählte Aktoren</h2>
<h2>2. Beobachtete Geräte</h2>
<p class="muted">Öffne „Details“, um Lernfortschritt, aktuelle Vorhersage und den automatisch gefundenen Kontext zu sehen.</p>
<div id="configured-actuators">Noch nicht geladen.</div>
</section>
<section class="wide">
<h2>Automatisch erkannter Lernkontext</h2>
<div id="actuator-detail" class="muted">Wähle einen Aktor aus der Liste.</div>
<h2>3. Lernfortschritt und Freigabe</h2>
<p class="muted">Die Freigabe erscheint erst, wenn genug eindeutig zugeordnete Handlungen gelernt wurden. Vorher bleibt das Gerät sicher im Beobachtungsmodus.</p>
<div id="actuator-detail" class="muted">Öffne bei einem beobachteten Gerät die Details.</div>
</section>
</main>
<script>
@@ -120,7 +151,7 @@ async function loadOverview() {
`<span class="chip">API: ${escapeHtml(health.status)}</span>`,
`<span class="chip">Lernsystem: ${escapeHtml(ml.status)}</span>`,
`<span class="chip">Aktoren: ${actuators.length}</span>`,
`<span class="chip">Aktive Modelle: ${reconciliation.trained_models}</span>`,
`<span class="chip">Lernbereite Geräte: ${reconciliation.trained_models}</span>`,
].join("");
} catch (error) {
status.innerHTML = `<p class="bad">${escapeHtml(error.message)}</p>`;
@@ -171,7 +202,7 @@ async function loadConfiguredActuators() {
const rows = await api("v1/actuators");
box.innerHTML = rows.length ? `
<table>
<tr><th>Aktor</th><th>Verhaltensmodell</th><th>Handlungen</th><th>Vorhersage</th><th></th></tr>
<tr><th>Gerät</th><th>Lernstatus</th><th>Gelernte Handlungen</th><th>Letzte Vorhersage</th><th>Aktionen</th></tr>
${rows.map(record => `
<tr>
<td>${escapeHtml(record.actuator_entity_id)}</td>
@@ -216,26 +247,27 @@ async function showActuator(actuatorId) {
<div>
<h3>${escapeHtml(record.actuator_entity_id)}</h3>
<p><strong>Status:</strong> <span class="${statusClass(record)}">${escapeHtml(lifecycleLabel(record))}</span></p>
<p><strong>Zuordnung:</strong> automatisch</p>
<p><strong>Sicherheit:</strong> ${Math.round(record.assignment.confidence * 100)} %</p>
<p><strong>Bewertung:</strong> ${escapeHtml(record.assignment.reason)}</p>
<p><strong>Kontextzuordnung:</strong> automatisch erledigt</p>
<p><strong>Zuordnungssicherheit:</strong> ${Math.round(record.assignment.confidence * 100)} %</p>
<p class="muted">Dieser Wert beschreibt, wie sicher Raum, Sensoren und Zustände zu diesem Gerät passen.</p>
<p><strong>Ergebnis:</strong> ${escapeHtml(record.assignment.reason)}</p>
</div>
<div>
<h3>Verhaltensmodell</h3>
<p><strong>Modus:</strong> ${escapeHtml(behaviorLabel(record))}</p>
<h3>Lernfortschritt</h3>
<p><strong>Betriebsart:</strong> ${escapeHtml(behaviorLabel(record))}</p>
<p><strong>Gelernte Handlungen:</strong> ${record.behavior.sample_count}</p>
<p><strong>Davon eindeutig Benutzer:</strong> ${record.behavior.high_confidence_sample_count}</p>
<p><strong>Letztes Training:</strong> ${escapeHtml(record.behavior.last_trained_at || "noch nicht")}</p>
<p><strong>Status:</strong> ${escapeHtml(record.behavior.reason)}</p>
<p><strong>Was noch passiert:</strong> ${escapeHtml(record.behavior.reason)}</p>
${activationButton}
<button class="secondary" onclick="evaluateActuator('${escapeHtml(record.actuator_entity_id)}')">Vorhersage jetzt prüfen</button>
</div>
</div>
<h3>Aktuelle Vorhersage</h3>
<h3>Was SillyHome aktuell vorhersagt</h3>
${prediction
? `<p><strong>${escapeHtml(prediction.target_state)}</strong> mit ${Math.round(prediction.confidence * 100)} % Sicherheit. ${escapeHtml(prediction.reason)} ${prediction.executed ? "<span class='ok'>Ausgeführt.</span>" : "<span class='muted'>Nicht ausgeführt.</span>"}</p>`
: "<p class='muted'>Aktuell ist kein gelerntes Handlungsmuster fällig.</p>"}
<h3>Automatisch verwendeter Kontext</h3>
<h3>Welche Zusammenhänge automatisch verwendet werden</h3>
${evidence ? `<ul>${evidence}</ul>` : "<p class='warn'>Noch kein geeigneter Kontext erkannt. SillyHome prüft bei neuen HA-Daten erneut.</p>"}
`;
} catch (error) {
@@ -276,7 +308,7 @@ async function removeActuator(actuatorId) {
await api(`v1/actuators/${encodeURIComponent(actuatorId)}`, {method: "DELETE"});
if (currentActuatorId === actuatorId) {
currentActuatorId = null;
document.getElementById("actuator-detail").textContent = "Wähle einen Aktor aus der Liste.";
document.getElementById("actuator-detail").textContent = "Öffne bei einem beobachteten Gerät die Details.";
}
await loadOverview();
} catch (error) {

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "sillyhome-next"
version = "0.5.0"
version = "0.5.1"
description = "Lokales Smart-Home-Intelligenzsystem für Home Assistant"
requires-python = ">=3.11"
dependencies = [

View File

@@ -0,0 +1,10 @@
from pathlib import Path
def test_addon_does_not_expose_internal_learning_parameters() -> None:
config = Path("addon/config.yaml").read_text(encoding="utf-8")
assert "\noptions:" not in config
assert "\nschema:" not in config
assert "prediction_confidence" not in config
assert "execution_cooldown_seconds" not in config

View File

@@ -9,7 +9,10 @@ def test_dashboard_is_served_at_root() -> None:
assert response.status_code == 200
assert "SillyHome Next" in response.text
assert "Aktor freigeben" in response.text
assert "ausdrücklichen Freigabe pro Aktor" in response.text
assert "So gehst du vor" in response.text
assert "Gerät zum Lernen auswählen" in response.text
assert "Wie gewohnt bedienen" in response.text
assert "Ohne deine spätere Freigabe wird nichts geschaltet" in response.text
assert "Du wählst keine Sensoren und erstellst keine Regeln" in response.text
assert "Automation-Entwurf" not in response.text
assert "Manuelle Overrides" not in response.text