Fridge Alert Bot
Telegram-Alarm, wenn ein Restaurant-Kühlschrank zu warm wird — und Ruhe, wenn nur jemand die Tür geöffnet hat.
- Rolle
- Eigenes Projekt — Konzept, Code, Tests
- Jahr
- 2026
- Kategorie
- Bots · Infrastruktur
- Technologien
- Python 3.12, FastAPI, aiogram 3, SQLite

Das Problem
Ein Kühlschrank, der sich über Nacht erwärmt, bedeutet verdorbene Lebensmittel am Morgen. Eine Lücke im Temperaturprotokoll bedeutet Ärger bei der Hygienekontrolle. Das Personal schaut ein paar Mal am Tag aufs Thermometer und verpasst so die meisten Ausfälle.
Günstige Temperaturfühler gibt es. Was fehlt, ist etwas, das sie überwacht und im richtigen Moment einen Menschen ruft — ohne jedes Mal Alarm zu schlagen, wenn jemand die Tür öffnet. Bei Steady Control arbeite ich mit Temperaturfühlern in Kühlschränken von Hotels und Restaurants, deshalb habe ich den Teil gebaut, der entscheidet, wann jemand geweckt wird.
Die Lösung
Die Fühler senden Messwerte an eine HTTP-API. Ein kleiner Zustandsautomat entscheidet, was jeder Messwert bedeutet:
- Ein Kühlschrank muss 5 Minuten über dem Limit bleiben, bevor ein Alarm kommt. Eine Tür, die eine Minute offen steht, löst nie Alarm aus.
- Ein Alarm pro Vorfall, danach eine „wieder normal"-Nachricht mit Dauer und Höchsttemperatur.
- Für die Entwarnung muss die Temperatur 0,5 °C unter dem Limit liegen, damit ein Kühlschrank, der genau am Limit pendelt, den Chat nicht überflutet.
- Verspätete oder doppelte Messwerte eines Fühlers, der offline war, landen in der Historie, setzen den Zustand aber nicht zurück. Vorfälle, die älter als 15 Minuten sind, werden gespeichert, aber nicht gemeldet.
In Telegram nutzt das Küchenteam /status, /history <sensor> (ein PNG-Diagramm), /setlimit und
/report, und jeden Morgen kommt eine Tageszusammenfassung.
Demo
Screenshots

Technologien
- Python 3.12
- FastAPI
- aiogram 3
- SQLite
- matplotlib
- pytest
- Docker
Python 3.12 mit Typannotationen, FastAPI für die API, aiogram 3 für den Bot, SQLite als Speicher und matplotlib für die Diagramme. API, Bot und Berichtsplaner laufen in einem asyncio-Prozess und teilen sich ein Service-Objekt, deshalb sind sie sich über den Zustand eines Kühlschranks immer einig.
Code im Detail
Die Alarmregel ist eine reine Funktion: keine Datenbank, kein Telegram, nur „Zustand + Messwert → neuer Zustand und vielleicht ein Ereignis". So wird jeder Grenzfall zu einem dreizeiligen Test.
def evaluate(
current: AlertState,
*,
sensor_id: str,
temperature: float,
at: datetime,
limit: float,
rules: AlertRules,
) -> tuple[AlertState, AlertEvent | None]:
"""Return the next state and, if something worth telling people happened, an event."""
above = temperature > limit
if current.state is SensorState.OK:
if above:
return AlertState(SensorState.PENDING, at, temperature), None
return current, None
started = current.breach_started_at
peak = (
temperature
if current.peak_temperature is None
else max(current.peak_temperature, temperature)
)
if current.state is SensorState.PENDING:
if not above:
return AlertState(), None
if at - started >= rules.delay:
event = AlertEvent(EventKind.ALERT, sensor_id, temperature, limit, started, peak, at)
return AlertState(SensorState.ALERT, started, peak), event
return replace(current, peak_temperature=peak), None
# ALERT
if temperature <= limit - rules.hysteresis:
event = AlertEvent(EventKind.RECOVERED, sensor_id, temperature, limit, started, peak, at)
return AlertState(), event
return replace(current, peak_temperature=peak), NoneEin Fehler, den ich gefunden und behoben habe
Der Simulator löste bei normalen Türöffnungen Fehlalarme aus
Was kaputt war
Ein Test schickt einen simulierten Tag voller Türöffnungen durch die echten Alarmregeln. Er schlug fehl: 18 Alarm- und Entwarnungsnachrichten ohne einen einzigen Geräteausfall. Über 30 zufällige Seeds erzeugte die Demo 8,2 Fehlalarme pro Tag. Eine Demo, die ständig falschen Alarm schlägt, lässt das Produkt kaputt aussehen.
Ursache
Der Simulator behandelte eine offene Tür als „der Fühler geht Richtung Raumtemperatur (24 °C)". So verhält sich ein frei hängender Luftfühler. Ein Fühler im Kühlschrank sitzt zwischen den Lebensmitteln und misst gemischte Luft. Zwei Türöffnungen kurz hintereinander hielten den Messwert länger als die Alarmverzögerung über 5 °C.
Die Lösung
Eine offene Tür hebt den Zielwert jetzt nur ein paar Grad über den Sollwert, und der Kompressor erholt sich schneller. Die Fehlalarme sanken bei denselben Seeds von 8,2 auf 0,13 pro Tag. Aus dem vagen Test wurden zwei genaue: Eine einzelne Türöffnung darf nie Alarm auslösen, und eine geschäftige Woche höchstens zwei.
door_tau_s: float = 900.0
cool_tau_s: float = 300.0
elif self.mode is Mode.DOOR_OPEN:
target, tau = p.ambient_c, p.door_tau_sdoor_delta_c: float = 5.0
door_tau_s: float = 300.0
cool_tau_s: float = 180.0
elif self.mode is Mode.DOOR_OPEN:
target, tau = p.setpoint_c + p.door_delta_c, p.door_tau_sErgebnis
- 83 automatische Tests und null Linter-Warnungen; GitHub Actions führt sie bei jedem Push aus.
- Läuft von Anfang bis Ende ohne Hardware: Der Simulator erzeugt Türöffnungen und Kompressorausfälle, und ohne Bot-Token erscheinen die Alarme in der Konsole.
- In einem Live-Durchlauf der Demo löste ein erzwungener Kompressorausfall genau einen Alarm aus, die Türöffnungen keinen.