fix(ha): cushion check - eliminate hallucinations + add quiet hours

Prompt fixes after Gemini was flagging empty terraces as having
cushions (count=4 on bare metal rattan chairs):
- Correct scene description: camera on left wall of house, terrace
  visible on right side (was incorrectly described as bird's-eye view)
- Explicit anti-hallucination guidance: don't assume textiles are
  there by default, empty terraces are common
- Add "DEFINITIVELY NOT TEXTILES" list (built-in rattan seats/backs,
  metal frames, shadows) - prior prompt only mentioned them indirectly
- Lead examples with "empty terrace" case so AI uses it as anchor
- Tiebreaker rule: when uncertain, default to has_loose_cushions=false

Quiet hours: no notify between 22:00-07:00 to avoid waking up. Snapshot
and AI evaluation still run and log, just no push/telegram during the
window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
blasebast 2026-06-01 12:27:09 +02:00
parent 6328a7c275
commit eb58e9c736
1 changed files with 44 additions and 23 deletions

View File

@ -43,37 +43,42 @@ COOLDOWN_HOURS = 3
FORECAST_PRECIP_THRESHOLD_MM = 0.5
RAIN_NOW_THRESHOLD_MM = 0.1
INSTRUCTIONS = """Analizujesz obraz z kamery zewnetrznej (widok z lotu ptaka na taras o kamiennej posadzce).
# Quiet hours - nie wysylaj powiadomien miedzy [START, END) godzina lokalna
# (22:00 - 07:00 = noc). Snapshot/AI nadal sie wykonuje (loguje), ale BEZ alertu.
QUIET_HOURS_START = 22 # 22:00
QUIET_HOURS_END = 7 # 07:00
KONTEKST SCENY:
Po PRAWEJ stronie kadru znajduje sie taras z meblami ogrodowymi. Glowny mebel do obserwacji to SOFA OGRODOWA z metalowym/wiklinowym stelazem, ktora ma KOMPLET TEKSTYLIOW: dwa MATERACE SIEDZISKOWE (dolne, plaskie, ciemne) plus trzy PODUSZKI OPARCIOWE (gorne, kwadratowe, ciemne). W tej scenie tekstylia sa CIEMNOSZARE / CZARNE.
INSTRUCTIONS = """Analizujesz obraz z kamery zewnetrznej zamontowanej na LEWEJ scianie domu, skierowanej w prawo i delikatnie w dol. W kadrze widac fragment domu, dach garazu, ogrod i taras o kamiennej posadzce. Glowny obszar zainteresowania - taras z meblami - znajduje sie PO PRAWEJ stronie kadru i nieco z gory (perspektywa z boku).
ZADANIE: Sprawdz czy te tekstylia (materace + poduszki) sa OBECNIE na sofie. Wszystkie sa luzne i moga sie zniszczyc od deszczu wiec trzeba je zabrac.
ZADANIE OBIEKTYWNE: Wykryj czy na meblach tarasowych po prawej stronie kadru leza LUZNE TEKSTYLIA (poduszki, materace, koce) ktore moga sie zniszczyc od deszczu.
CO LICZYC jako tekstylia (rosnij count o 1 za kazdy widoczny element):
- Materac siedziskowy luzno polozony na ramie sofy
- Poduszka oparciowa lub dekoracyjna na sofie/oparciu
- Dodatkowo: luzne koce, narzuty, mniejsze poduszki dorzucone
WAZNE: NIE zakladaj z gory ze tekstylia tam sa. Bywaja dni gdy taras ma TYLKO same metalowe/wiklinowe meble bez zadnych tekstyliow - to normalna, czesta sytuacja. W takim wypadku has_loose_cushions=false.
CZEGO NIE LICZYC (False positives - traktuj jako NIE):
- Same metalowe / wiklinowe / drewniane elementy konstrukcji mebli BEZ tekstyliow
- Plandeka zakrywajaca mebel (jesli widac plandeke - tekstylia juz zabezpieczone)
- Doniczki, donice, kamienie, posadzka, dywany podlogowe
- Cienie, plamy swiatla
- Krzesla po przeciwnej (lewej) stronie tarasu
OBIEKTYWNE KRYTERIA WYKRYCIA TEKSTYLIOW:
Tekstylia (poduszka, materac, koc) to MIEKKI, plaski, prostokatny lub kwadratowy element o WYRAZNYM ksztalcie wyrastajacym ponad rame mebla. Powinien byc widoczny jako oddzielna, dorzucona warstwa o miekkich krawedziach.
ZASADA: Jesli na ramie sofy po prawej WIDAC ciemne/kolorowe miekkie elementy - to sa tekstylia, has_loose_cushions=true. Jesli widac sama goly metalowy/wiklinowy stelaz bez tekstyliow - has_loose_cushions=false.
DEFINITYWNIE NIE SA TEKSTYLIAMI (traktuj jako has_loose_cushions=false):
- Czarne/ciemne plastikowe lub gumowe siedziska wlasciwe metalowych krzesel ogrodowych (sa CZESCIA konstrukcji, nie luzne)
- Czarne tkane oparcia w stylu rattan/wiklina - to konstrukcja krzesla
- Same metalowe ramy / siedziska siatkowe / drewniane deski
- Ciemne cienie pod meblami
- Doniczki, kamienie, posadzka
UWAGA NA HALUCYNACJE: Jesli obraz pokazuje TYLKO dwa metalowe krzesla z prostym czarnym oparciem i siedzeniem przy stoliku - to NIE SA poduszki, to konstrukcja krzesla. Has_loose_cushions=false.
ZASADA ROZSTRZYGAJACA: Jesli watpisz czy widzisz luzny tekstylny dodatek czy tylko czesc konstrukcji krzesla - ustaw has_loose_cushions=false. Lepiej raz przegapic alert (mozna zauwazyc samemu przed deszczem) niz codziennie spamowac falszywymi powiadomieniami.
Odpowiedz w formacie strukturalnym (JSON), pola:
- has_loose_cushions (bool): true gdy na sofie po prawej sa widoczne JAKIEKOLWIEK tekstylia (materace, poduszki, koce)
- count (int): laczna liczba widocznych tekstyliow (materace + poduszki + dodatkowe). Sofa w komplecie = 5 (2 materace + 3 poduszki)
- has_loose_cushions (bool): true TYLKO gdy widzisz wyraznie odrebne, miekkie elementy lezace na ramach mebli (nie sama konstrukcje)
- count (int): liczba widocznych LUZNYCH tekstyliow (0 jesli has_loose_cushions=false)
- confidence (int 0-100): pewnosc oceny
- description (string, po polsku, max 150 znakow): co konkretnie widzisz na sofie po prawej
- description (string, po polsku, max 150 znakow): konkretny opis tego co widzisz - mebli, materialow, ich stanu
PRZYKLAD KOMPLET: has_loose_cushions=true, count=5, confidence=90, description="Sofa po prawej w komplecie - widoczne 2 materace siedziskowe i 3 poduszki oparciowe"
PRZYKLAD CZESCIOWY: has_loose_cushions=true, count=2, confidence=85, description="Na sofie po prawej widoczne tylko 2 materace, brak poduszek oparciowych"
PRZYKLAD PUSTY: has_loose_cushions=false, count=0, confidence=90, description="Sofa po prawej pusta - widac sam metalowy stelaz, tekstylia zabrane"
PRZYKLAD ZASLONIETY: has_loose_cushions=false, count=0, confidence=80, description="Sofa zakryta plandeka - tekstylia juz zabezpieczone"
PRZYKLAD PUSTY TARAS (typowy): has_loose_cushions=false, count=0, confidence=90, description="Dwa metalowe krzesla ogrodowe z czarnymi wbudowanymi oparciami i stolik. Brak luznych tekstyliow."
PRZYKLAD PUSTA SOFA: has_loose_cushions=false, count=0, confidence=85, description="Stelaz sofy widoczny bez tekstyliow - same metalowe rame i drewniane elementy."
PRZYKLAD Z PODUSZKAMI: has_loose_cushions=true, count=3, confidence=85, description="Na sofie trzy luzne kwadratowe poduszki dekoracyjne w jasnym kolorze, wyraznie odstajace od ciemnej ramy."
PRZYKLAD KOMPLET: has_loose_cushions=true, count=5, confidence=90, description="Sofa wyposazona - widoczne 2 plaskie materace siedziskowe plus 3 mniejsze poduszki oparciowe."
PRZYKLAD ZASLONIETY: has_loose_cushions=false, count=0, confidence=80, description="Sofa zakryta plandeka ochronna - tekstylia juz zabezpieczone."
"""
AI_STRUCTURE = {
@ -132,6 +137,16 @@ def _on_cooldown():
return False
def _in_quiet_hours():
"""True jesli teraz jest cisza nocna (alert idzie tylko do logu, bez notify/telegram)."""
h = datetime.now().hour
if QUIET_HOURS_START < QUIET_HOURS_END:
# zakres w obrebie tego samego dnia, np. 0-7
return QUIET_HOURS_START <= h < QUIET_HOURS_END
# zakres przez polnoc, np. 22-7 (typowy nocny)
return h >= QUIET_HOURS_START or h < QUIET_HOURS_END
def _rain_now():
try:
return float(state.get(RAIN_NOW_SENSOR) or 0) >= RAIN_NOW_THRESHOLD_MM
@ -298,7 +313,13 @@ def _run_check(reason):
)
if has_cushions and count > 0 and confidence >= MIN_CONFIDENCE_FOR_ALERT:
_notify(count, confidence, desc, reason)
if _in_quiet_hours():
log.info(
f"cushion_check: ALERT POMINIETY (quiet hours {QUIET_HOURS_START}:00-{QUIET_HOURS_END}:00) - "
f"count={count} conf={confidence}% desc={desc[:120]}"
)
else:
_notify(count, confidence, desc, reason)
else:
log.info(
f"cushion_check: brak alertu (has_cushions={has_cushions}, "