Wie ich meiner Garagenkamera beigebracht habe, an die Müllabfuhr zu denken
Update, September 2026. Ich habe das seit der Veröffentlichung zweimal umgebaut. Die kalibrierten Sektoren aus Schritt 2 laufen in meiner Garage nicht mehr — sie sind auf eine Art gescheitert, die ich nicht kommen sah, und ihr Nachfolger ebenfalls. Der ursprüngliche Text steht unverändert darunter, weil die beiden Sackgassen der lehrreiche Teil sind. Wer etwas kopieren will, das funktioniert, liest zuerst was gebrochen ist und was jetzt läuft.
Das Problem
Müllabfuhr-Tage haben mich immer wieder kalt erwischt. Verpasst man die Abholung einer der vier Tonnen, sitzt man zwei Wochen auf einem überquellenden Behälter — Bio, Gelber Sack, Papier, Restmüll, alle gleich unangenehm wenn sie voll sind. Ich hab Kalender-Erinnerungen probiert, Smart-Speaker-Pings, einen Post-it-Zettel an der Küchenarbeitsplatte. Nichts war perfekt.
Was ich brauchte, war keine weitere Erinnerung. Ich brauchte ein System, das weiß, ob die Tonne schon draußen steht — und mich nur dann nervt, wenn nicht.
Das Setup auf einen Blick
- Reolink-Garagenkamera — war schon da, 24/7 RTSP, sieht die vier Tonnen an der Rückwand stehen
- GPT-4o Vision — bekommt jeden Abend vor der Abholung ein Bild und liefert zurück, welche Tonnen noch drinnen sind
- Abfallkalender-Integration für Home Assistant — weiß, was morgen abgeholt wird
- n8n — orchestriert die Kette Snapshot → Vision → Kalender → Benachrichtigung
- Telegram-Bot — schickt das echte Foto mit, damit ich es selbst sehen kann
Schritt 1: Der Snapshot
Die Garagenkamera liefert bereits einen RTSP-Stream. In Home Assistant heißt sie camera.garage. Der erste Schritt ist einfach: ein Standbild holen:
// Im n8n Code-Node
const ha_token = fs.readFileSync('/pfad/zum/ha_token','utf8').trim();
const snapshot = await fetch('http://homeassistant:8123/api/camera_proxy/camera.garage', {
headers: { 'Authorization': 'Bearer ' + ha_token }
});
const buffer = Buffer.from(await snapshot.arrayBuffer());
const b64 = buffer.toString('base64');
Schritt 2: Der KI sagen, wo sie hinschauen soll (genau das habe ich später wieder ausgebaut)
Meine erste Version war naiv: ich hab das ganze Garagenfoto an GPT-4o geschickt und gefragt „Welche der vier Tonnen siehst du?“. Das funktionierte vielleicht in 70% der Fälle. Das Modell verwechselte den Gelben Sack mit einer Kiste, halluzinierte Tonnen die gar nicht da waren, übersah die Biotonne hinter dem Fahrrad. Für eine Benachrichtigung, der ich vertrauen muss, unbrauchbar.
Der Fix: manuell kalibrierte Bounding Boxes. Die Tonnen stehen immer an denselben vier Stellen an der Rückwand. Ich habe diese Stellen einmal auf einem Referenzbild (640×480) ausgemessen und als Sektoren hardcoded:
const SECTORS = {
A: { x:10, y:165, w:140, h:235, label: "Altpapier", color: "blau" },
B: { x:145, y:170, w:95, h:210, label: "Restmüll", color: "schwarz" },
C: { x:240, y:110, w:80, h:215, label: "Bio", color: "braun" },
D: { x:315, y:120, w:110, h:250, label: "Wertstoff", color: "gelb" },
};

Die Kalibrierung ist eine einmalige Sache: Snapshot machen wenn alle vier Tonnen in Position stehen, jeden Sektor in einem Bildeditor ausmessen, Koordinaten reinkopieren. Damals habe ich geschrieben, diese fünf Minuten verdoppelten die Genauigkeit. Das galt etwa sechs Wochen — unten steht der Tag, an dem es nicht mehr galt.
Der Workflow skaliert dann jeden neuen Snapshot auf 640×480 (damit die Sektoren stabil bleiben, auch wenn die Kameraauflösung sich ändert) und schneidet pro Sektor ein Bild mit ffmpeg in/aus dem Speicher heraus — keine temporären Dateien:
function ffmpeg(inputBuf, vfArg) {
return new Promise((resolve, reject) => {
const ff = spawn("ffmpeg", ["-loglevel","error","-i","pipe:0","-vf",vfArg,"-f","mjpeg","-q:v","5","pipe:1"]);
const chunks = [];
ff.stdout.on("data", c => chunks.push(c));
ff.on("close", code => code === 0 ? resolve(Buffer.concat(chunks)) : reject(code));
ff.stdin.end(inputBuf);
});
}
const baseBuf = await ffmpeg(snapshotBuffer, "scale=640:480");
const cropBuf = await ffmpeg(baseBuf, `crop=${spec.w}:${spec.h}:${spec.x}:${spec.y}`);
Jedes zugeschnittene Bild ist jetzt eine exakte Aufnahme genau eines Platzes. Der GPT-4o-Call ist entsprechend einfach — statt „finde vier Tonnen in dieser Szene“ beantwortet er eine binäre Frage: steht in diesem Platz eine Tonne, ja oder nein?
const prompt = `Du siehst ein eng zugeschnittenes Bild eines kleinen Bereichs in einer Garage.
Beschreibe ZUERST in 1-2 Sätzen was du WIRKLICH siehst (Boden? Wand? Tür? Plastikbehälter? Fahrrad?). Erfinde NICHTS.
Dann entscheide:
- bin_visible=TRUE NUR wenn du DEUTLICH einen Mülltonnen-Korpus (rechteckiger Plastikbehälter mit Rädern unten und einem klar abgesetzten Deckel oben) siehst, der MEHR als die Hälfte des Bildes einnimmt
- bin_visible=FALSE wenn das Bild hauptsächlich Tür, Wand, Boden, Schatten, Fahrrad oder andere Gegenstände zeigt
- bin_visible=FALSE bei Unsicherheit
Antworte NUR als JSON, keine Erklärungen außerhalb:
{"what_i_see": "konkrete Beobachtung in 5-10 Wörtern", "bin_visible": true/false}`;
Die vier Sektor-Calls laufen parallel — etwa 2 Sekunden Ende-zu-Ende. Das Zurück-Mapping auf den Tonnen-Typ ist trivial, weil jeder Sektor sein Label schon dranhängt.
Drei kleine Entscheidungen, die einen Unterschied machen:
- Erzwinge ein „what_i_see“ Feld vor dem Boolean. Das Modell beschreiben zu lassen, was es tatsächlich im Crop sieht, BEVOR es bin_visible entscheidet, drückt Halluzinationen — es muss sich erst auf „ich sehe eine Tür“ festlegen, bevor es „Tonne sichtbar: true“ behaupten kann
- Explizit auf FALSE biasen bei Unsicherheit. Eine verpasste Warnung (False Negative) ist okay — ich sehe die Tonne bei meinem nächsten Vorbeigehen noch in der Garage stehen. Eine falsche „Tonne ist draußen“-Nachricht würde mein Vertrauen in das ganze System zerstören
- temperature: 0 + max_tokens: 150. Stabil, parsbar, günstig — etwa 0,5 Cent pro Abend-Check über alle vier Sektoren
Schritt 3: Der Kalender-Abgleich
Home Assistant zieht den lokalen Abholplan über die Waste Collection Schedule Integration. Die relevanten Sensoren sehen so aus:
sensor.naechste_biotonne_abholung // "in 1 day"
sensor.naechste_restmuell_abholung // "in 10 days"
sensor.naechste_papier_abholung // "in 5 days"
sensor.naechste_gelber_sack_abholung // "in 3 days"
Der Workflow läuft jeden Abend um 20:45 und prüft, ob einer dieser Sensoren „in 1 day“ sagt. Wenn ja, muss diese Tonne vor dem Morgen draußen stehen.
Schritt 4: Die Foto-Benachrichtigung
Wenn die relevante Tonne im bins_visible-Ergebnis des Vision-Calls noch auftaucht, schickt Telegram den Alarm mit dem echten Snapshot als Anhang:
🟤 Mülltonnen-Check: Biotonne (morgen)
⚠️ Die Biotonne steht noch in der Garage!
Bitte JETZT rausstellen!
📷 Es stehen alle vier Tonnen noch in der Garage incl. der
Biomülltonne, die morgen abgeholt wird.
Das Foto ist das, was das Ganze zum Funktionieren bringt. Einen Text-Alert kann man leicht mit „ich hab sie doch schon rausgestellt“ wegwischen. Ein Foto, auf dem die Tonne buchstäblich noch da steht, nicht.
Was gebrochen ist — zweimal
Die Sektoren liefen wunderbar, bis jemand die Biotonne an den falschen Platz zurückgestellt hat. Was in einer Garage, die eine Familie teilt, kein Sonderfall ist. Das ist ein Dienstag.
Der Fehler ist keiner, den man nachjustieren kann, sondern einer im Entwurf: Ein Sektor beschreibt eine Position, und ich habe ihn benutzt, um eine Identität zu meinen. Sektor D war nur deshalb „die Wertstofftonne“, weil die Wertstofftonne dort üblicherweise stand. In dem Moment, in dem stattdessen die Biotonne dort stand, meldete das System, die Wertstofftonne stehe noch in der Garage — während sie längst an der Straße war. Ein selbstbewusster Alarm über eine Tonne, die nicht da war.
Den zweiten Fehler hatte ich im Originaltext sogar als Stärke verkauft: Ich schrieb, die Prüfung pro Sektor funktioniere nachts, weil das Modell nur ein tonnenförmiges Objekt erkennen müsse. Übersehen hatte ich, dass meine Sektordefinition auch ein color-Feld trägt — und nach Einbruch der Dunkelheit schaltet die Kamera auf Infrarot und liefert Graustufen. In einem Graustufenbild gibt es kein Gelb. Die halbe Erkennungslogik lief jeden Abend still ins Leere, also genau dann, wenn die Prüfung stattfindet.
Umbau eins: nicht mehr verorten, sondern zählen
Also habe ich die Sektoren weggeworfen und eine Frage gestellt, die nicht davon abhängt, wo etwas steht. Vier Tonnen leben in dieser Garage. Wenn morgen abgeholt wird und ich zähle immer noch vier, ist nichts rausgestellt. Zähle ich weniger, steht die fällige Tonne schon draußen.
Das gibt absichtlich Information auf — es weiß nicht mehr, welche Tonne fehlt. Muss es auch nicht. Der Abfallkalender weiß längst, was fällig ist; die Kamera muss nur beantworten, ob überhaupt etwas die Garage verlassen hat.
// Vier Tonnen leben dauerhaft in der Garage: Papier, Rest, Bio, Wertstoff.
const BASELINE = 4;
// weniger als Baseline -> faellige Tonne ist schon draussen -> still bleiben
// gleich Baseline -> nichts rausgestellt -> Alarm senden
// Antwort unlesbar -> ueberspringen, niemals raten
Die letzte Zeile ist wichtiger als die beiden darüber. Ein Vision-Modell liefert gelegentlich etwas, das sich nicht auswerten lässt. Die naheliegende Reaktion wäre, das als „keine Tonnen gesehen“ zu behandeln und zu alarmieren. Bloß nicht. Ein Fehlalarm kostet Vertrauen, eine verpasste Erinnerung nicht — ich stelle die Tonne lieber einmal selbst raus, als dauernd wegen einer Tonne angepiepst zu werden, die schon am Bordstein steht.
Umbau zwei: wenn der Fehler in die falsche Richtung zeigt
Im September wurden die Tonnen erneut umgestellt, diesmal zu zwei engen Paaren. Im Infrarotbild ist ein enges Paar dunkler Tonnen ein einziger dunkler Klumpen. Die Frage „zähle jede einzelne Tonne“ lieferte reproduzierbar 3 statt 4 — auf einem Bild, auf dem ich selbst alle vier sehen konnte.
Und jetzt schau, in welche Richtung dieser Fehler zeigt. Drei ist weniger als die Baseline von vier, und weniger heißt „die fällige Tonne ist schon draußen“ — das System bleibt also still. Die Fehlzählung erzeugte keinen falschen Alarm. Sie erzeugte gar keinen, am Abholtag, mit der Tonne noch in der Garage, und nirgends im Protokoll stand etwas von einem Problem. Ein Fehler, der schreit, ist lästig. Der gefährliche ist der, der verstummt — und dieses System war eine unbekannte Anzahl von Abenden lang still im Unrecht.
Die Lösung war kein besseres Modell. Es war, das Modell zum zweimaligen Hinsehen zu zwingen, indem das Bild in Hälften geteilt und jede Hälfte einzeln abgefragt wird:
Du siehst das Innere einer Garage (Nachtaufnahme, Graustufen/IR).
Zaehle die MUELLTONNEN.
NICHT mitzaehlen: Fahrraeder, Roller, Kartons, Besen, Schlaeuche,
Regale, Werkzeug, Schatten, Rohre.
Gehe das Bild in zwei Haelften durch: LINKE Bildhaelfte und RECHTE.
Nenne je Haelfte die gefundenen Tonnen einzeln. Tonnen stehen dicht
beieinander und verdecken sich teilweise.
Antworte NUR als JSON: {"links":["..."],"rechts":["..."],"count":<Summe>}
Gleiches Modell, gleiches Bild, gleiche Temperatur. Auf dem Bild, das vorher versagt hatte, lag der alte Prompt in 0 von 2 Läufen richtig, der Hälften-Prompt in 5 von 5. Die Tonnen pro Hälfte einzeln aufzuzählen, bevor eine Summe kommt, ist bewusst doppelte Arbeit — das Modell kann ein Paar nicht zu einem Klumpen zusammenziehen, wenn es die Mitglieder jeder Hälfte laut benennen muss.
Was ich meinem Juni-Ich sagen würde
Frag nach, woraus deine Annahme gemacht ist. „Die Wertstofftonne steht in Sektor D“ sieht aus wie eine Tatsache über Tonnen. Es ist eine Tatsache über Möbelstellung, und Möbel wandern. Was ein Mensch mit der Hüfte verschieben kann, taugt nicht als Schlüssel.
Und frag, wohin deine Fehler zeigen. Die erste Fassung habe ich damit verbracht, Genauigkeit als eine einzige Zahl zu jagen. Die Zahl, auf die es ankam, war nie 70 % oder 95 % — es war die Frage, ob eine falsche Antwort das System schreien oder verstummen lässt. Beide Umbauten sind aus dieser Frage entstanden, und der zweite hat einen Fehler gefunden, der mich noch nichts gekostet hatte und es irgendwann getan hätte, an einem Dienstagmorgen.
Was ich gelernt habe
Eine kleinere Frage schlägt ein größeres Modell. Das ist die eine Lehre, die beide Umbauten überlebt hat. Jede Fassung, die funktioniert hat, funktionierte, weil ich eingegrenzt habe, was das Modell auf einmal entscheiden muss — ein Sektor statt einer Szene, später eine Bildhälfte statt eines ganzen Bildes. Kein einziges Genauigkeitsproblem habe ich gelöst, indem ich zu einem besseren Modell gegriffen habe.
Check am Abend davor, nicht am Morgen selbst. Meine erste Version lief um 06:00 Uhr am Abholtag. Sinnlos — da trank ich schon Kaffee und der Müllwagen war auf halbem Weg die Straße runter. Der Abend-Check gibt mir Zeit, das während normaler Haushaltsstunden zu erledigen.
Foto-Beweis schlägt Text-Beweis, jedes Mal. Sobald der Alarm ein Foto der Tonne enthält, die noch in der Garage steht, gibt es keine Diskussion. Ich stehe auf und schiebe sie raus.
Ergebnis
Zwei Wochen später: null verpasste Abholungen. Ein wirklich befriedigendes „hab ich dir doch gesagt“ jeden Abend vor dem Abholtag — von einem Smart Home, das einfach seinen Job macht.
Schreibe einen Kommentar