# What to Wear (WTW) — Design-Spec v1 - **Datum:** 2026-07-11 - **Status:** Entwurf zur Review (Brainstorming-Ergebnis; Keim für den BMAD-PRD) - **Methode:** AI-Dev-Method (BMAD + adversariale Reviewer-Linsen + KI-Zweitkritiker) - **Domain / Name:** `what_to_wear` — „What to Wear (WTW)" --- ## 1. Intent (ein Satz) Eine **quelloffene Home-Assistant-Integration**, die dem Nutzer abends sagt, **welche konkreten Kleidungsstücke aus seinem Kleiderschrank** er für morgen rauslegen soll — deterministisch aus der Wetterprognose abgeleitet, optional sprachlich schön formuliert, ausgespielt über Dashboard / Push / Ansage. ## 2. Problem & Zielgruppe - **Problem:** Morgens Zeitdruck und Fehlgriffe bei der Kleidung; abends fehlt der schnelle Überblick, was das Wetter *morgen* konkret verlangt (Regen, Frost, Wind, UV) — und was davon man überhaupt besitzt. - **Zielgruppe:** Home-Assistant-Nutzer (DACH + international), die abends Kleidung vorbereiten — Pendler, Eltern (Kind anziehen), Menschen mit festem Morgenritual. - **Vertriebskontext:** Open-Source-HACS-Produkt (Reichweite/Reputation), später Upsell über einen optionalen LagerLens-Connector (KI-Foto-Katalogisierung der Kleidung). ## 3. Umfang v1 — bewusste Nicht-Ziele (v1.1+) **In v1:** - HACS-Custom-Integration `what_to_wear`, Config-Flow, Options-Flow. - Kleiderschrank-Inventar **nativ** über HA-Sub-Entries (echte Stücke mit Eigenschaften). - `WeatherProvider`-Interface mit **einer** Implementierung: HA-Wetter-Entität. - Pipeline: Normalizer → Regel-Engine → Matcher → (optional) LLM-Ton. - Ausgabe: **Empfehlungs-Sensor** + Service/Event; **read-only Lovelace-Karte**; **2 Blueprints** (Push, TTS). - Optionaler **LLM-Ton** (Bring-your-own-Key, default AUS). - i18n **de + en**. **Bewusst vertagt (v1.1+):** - LagerLens-Connector als zweite Kleiderschrank-Quelle. - Open-Meteo als zweiter `WeatherProvider`. - Schöne CRUD-Verwaltungskarte für den Kleiderschrank (v1: native HA-UI genügt). - Mehrere Personen/Profile; Aktivitäts-/Kalenderkontext (Termine, „heute Sport"); Wäsche-/Verfügbarkeitsstatus. ## 4. Produktform & Distribution - Standard-HA-`custom_component` (Python, async), HACS-installierbar; MVP-Struktur: ``` custom_components/what_to_wear/ __init__.py, manifest.json, config_flow.py, coordinator.py, sensor.py, services.yaml, strings.json, translations/{de,en}.json, weather/ (provider-interface + ha_entity impl), logic/ (normalize.py, rules.py, matcher.py, phraser.py) blueprints/automation/what_to_wear/ notify_push.yaml, announce_tts.yaml www/what-to-wear-card.js (read-only Lovelace-Karte) ``` - **Lizenz:** Vorschlag **MIT** (offene Kleinentscheidung). - **Min-HA-Version:** Vorschlag **2024.12+** (für `weather.get_forecasts` mit `return_response=True` und Config-Sub-Entries) — in der Architektur-Phase gegen die tatsächlich benötigten APIs verifizieren. - Repo: **lokal** (git `master`), Remote später. ## 5. Architektur — Pipeline & Komponenten ``` WeatherProvider ─▶ ForecastNormalizer ─▶ RulesEngine ─▶ WardrobeMatcher ─▶ (opt.) LLMPhraser │ RecommendationSensor ◀───────────────────────────────────┘ │ (User-Automation via Blueprint) ─▶ Push / TTS / Lovelace-Karte ``` - **WeatherProvider** (Interface `async get_tomorrow_forecast() -> RawForecast`): v1-Impl liest die vom Nutzer gewählte `weather.*`-Entität via `weather.get_forecasts` (daily + hourly, `return_response=True`). - **ForecastNormalizer:** RawForecast → **NormalizedForecast** für *morgen* (lokale TZ), interne **SI-Einheiten**, je Feld ein „vorhanden?"-Flag. Aggregiert stündlich → Tageswerte nach lokalem Datum. - **RulesEngine:** NormalizedForecast → Liste **Requirement** (Eigenschaft + Grund + Priorität). - **WardrobeMatcher:** Requirements × Kleiderschrank → gewählte Stücke + Begründung + **Lücken** („keine Regenjacke"). - **LLMPhraser** (optional): formt nur den Text; bei Aus/Fehler → deterministischer Regeltext. - **DataUpdateCoordinator:** hält das morgige Ergebnis aktuell; Benachrichtigung triggert der Nutzer per Blueprint. ### Invarianten-Spine (Keim für die Architektur-Phase) - **INV-1 (Nie stumm):** Die Empfehlung fällt nie aus. Bei Teil-Ausfall (LLM, fehlendes Wetterfeld, leerer Schrank) liefert der deterministische Kern trotzdem eine Empfehlung und **benennt die Lücke**. - **INV-2 (Morgen lokal):** „Morgen" ist stets das lokale Kalenderdatum in `hass.config.time_zone` (DST-fest). - **INV-3 (SI intern):** Interne Berechnung nur im SI-Normmodell; Quelle/Einheit/Missingness bleiben am Datum erhalten; Werte werden **nie** aus Feldname oder Größenordnung geraten. - **INV-4 (Geheimnisse):** LLM-Key (später LagerLens-Token) nie im Klartext in Logs/State/Attributen; nur im HA-Config-Storage. - **INV-5 (Keine stille Netzlast):** v1 spricht nur mit HA selbst. Externe Provider (v1.1) nur opt-in, mit Coordinator/Timeout/Backoff/Attribution; kein stiller Provider-Wechsel. - **INV-6 (Trennung):** Normalize / Rules / Matcher sind strikt getrennt und je einzeln testbar. ## 6. Datenmodell - **Kleidungsstück (Sub-Entry):** `name`, `kategorie` (z. B. Jacke/Pullover/Hose/Schuhe/Accessoire/Kopf/Hand), `waerme` 1–5, `wasserdicht` bool, `winddicht` bool, `formalitaet` (leger/business), `saison`/Temp-Range (optional), `foto` (optional), `aktiv` bool. - **NormalizedForecast (morgen):** `temp_min`, `temp_max`, `temp_gefuehlt_min/max` (°C), `regen_wkt` (%), `regen_menge` (mm), `wind` / `boeen` (km/h), `uv_index`, `zustand` (klar/bewölkt/Regen/Schnee/Gewitter) — **jeweils mit `vorhanden`-Flag** + Herkunft/Zeitstempel. - **Requirement:** `eigenschaft` (z. B. `wasserdichte_aussenschicht`, `waerme_min:4`, `sonnenschutz`, `handschuhe`, `muetze`), `grund` (menschlesbar), `prioritaet` (muss/soll/kann). - **Recommendation:** `kurztext` (Sensor-State), `stuecke[]`, `requirements[]`, `luecken[]`, `kennwerte`, `quelle`. ## 7. Regel-Engine (illustrative Schwellwerte — final in PRD/Architektur) > Beispielhaft, konfigurierbar; **nicht** endgültig. Kälteempfinden-Offset verschiebt Temperaturschwellen. - `temp_gefuehlt_max < 0 °C` → `waerme_min:5`, `muetze`, `handschuhe`, `schal`. - `0–8 °C` → `waerme_min:4`; `8–15 °C` → `waerme_min:3`; `15–22 °C` → `waerme_min:2`; `> 22 °C` → `waerme_min:1`, ggf. `sonnenschutz`. - `regen_wkt ≥ 40 %` **oder** `regen_menge ≥ 1 mm` → `wasserdichte_aussenschicht`, `feste_schuhe`. - `boeen ≥ 40 km/h` → `winddicht`. - `uv_index ≥ 6` → `sonnenschutz` (Brille/Kopfbedeckung). - `zustand = Schnee` → `waerme_min:4`, `wasserdicht`, `profilsohle`. ## 8. Matcher - Wählt je Requirement das am besten passende **aktive** Stück (Priorität muss > soll > kann; bei Gleichstand nächstpassende Wärme/Formalität). - Fehlt ein Stück für ein `muss`/`soll`-Requirement → **Lücke** in `luecken[]` (z. B. „Für morgen wäre eine Regenjacke ideal — keine im Schrank hinterlegt."). - Ergebnis ist erklärbar: jedes Stück trägt seinen Grund (welches Requirement es erfüllt). ## 9. LLM-Ton (optional, Bring-your-own-Key) - Default **AUS**. Nutzer trägt eigenen Key (OpenAI/Anthropic) im Options-Flow ein → nur im Config-Storage. - Eingabe an das LLM: die **strukturierte** Recommendation (Stücke + Gründe + Kennwerte) + Zielsprache. Aufgabe: nur **umformulieren**, keine neuen Fakten, keine geänderte Stückauswahl. - **Fehler/Timeout/kein Key → deterministischer Regeltext** (INV-1). LLM ändert nie die Auswahl. ## 10. Ausgabe: Sensor / Service / Karte / Blueprints - **Sensor** `sensor.what_to_wear`: `state` = Kurzempfehlung; `attributes` = `stuecke`, `requirements`, `luecken`, `kennwerte`, `quelle`, `stand`. - **Service/Event** `what_to_wear.recommend` (manuelles Neuberechnen; feuert Event mit dem Ergebnis). - **Lovelace-Karte** (read-only): zeigt morgige Empfehlung + Stücke + Lücken dauerhaft. - **Blueprints:** `notify_push` (Nutzer wählt notify-Ziel + Uhrzeit), `announce_tts` (Nutzer wählt media_player + Auslöser). Import ist ein geführter HA-Klick-Flow → laienfreundlich, ohne notify-Logik in der Integration. ## 11. Konfiguration - **Config-Flow (Ersteinrichtung):** Wetter-Entität wählen (Entity-Selector, Pflicht; Fehler wenn keine da), Sprache (Default = HA-Sprache), Kälteempfinden-Offset. - **Options-Flow:** Schwellwerte/Offsets, optionaler LLM-Key + Provider (default aus). - **Sub-Entries:** Kleidungsstücke anlegen/bearbeiten/löschen in der nativen HA-UI. ## 12. Fehlerbehandlung & Edge Cases (aus dem Kritiker-Review) - Keine geeignete `weather.*`-Entität → klarer Config-Fehler, kein Raten (INV-1/Config). - Wetterfeld fehlt → Regel greift **degradiert** + Hinweis; „fehlend" ≠ „0" (INV-3). - „Morgen"/DST strikt in lokaler TZ (INV-2). - LLM-Fehler/Timeout/kein Key → Fallback auf Regeltext (INV-1). - Leerer/lückenhafter Schrank → Anforderung + Lücke nennen statt schweigen (INV-1). - Einheiten (°C/°F, km/h/mph, mm/inch) bewusst konvertieren; Open-Meteo-Wettercode ↔ HA-`condition` per Mapping-Tabelle (v1.1). ## 13. i18n - HA-Translations für UI (`de`, `en`); Empfehlungstext folgt der HA-Sprache; Regeltext-Bausteine je Sprache. ## 14. Tests & echte Verifikation (Methoden-Pflicht) - **Unit:** Normalizer/Rules/Matcher mit Wetter-Fixtures (Regen, Frost, Hitze, DST-Tag, fehlende Felder, leerer Schrank). - **Integration:** `pytest-homeassistant-custom-component` (Config-Flow, Sensor, Service). - **Echte E2E:** Wegwerf-HA-Docker-Instanz — Integration installieren → Wetter-Entität + Beispielschrank → Sensor prüfen → Blueprint-Push real auslösen. Negativfälle (keine Wetter-Entität, LLM-Key falsch). - **TDD:** Test rot → Code → grün → volle Suite → Commit (ein Commit pro Story). qwen/Cloud-Review nach jeder Story. ## 15. Offene Punkte / Annahmen (in PRD/Architektur klären) - Lizenz (Vorschlag MIT). Min-HA-Version (Vorschlag 2024.12+, gegen APIs verifizieren). - Ob v1 die Sub-Entry-CRUD-UX von HA praktisch trägt (sonst früher CRUD-Karte). - Genaue Schwellwerte/Regeln (Kap. 7 illustrativ). - Foto-Speicherung bei nativem Kleiderschrank (Pfad/Umfang) — v1 optional/minimal. ## 16. Roadmap v1.1+ LagerLens-Connector (Foto-Katalog) · Open-Meteo-Provider · CRUD-Verwaltungskarte · Mehrpersonen-Profile · Aktivitäts-/Kalenderkontext · Wäsche-/Verfügbarkeitsstatus.