Zum Inhalt
BoilerBuddy

API

Puffern und die Uhr

Messwerte lokal behalten und der Reihe nach senden, nach einem Ausfall innerhalb der Limits aufholen und Messwerte von vor der Uhrsynchronisierung korrigieren lassen.

So misst dein Gerät weiter, während das Internet weg ist, holt danach auf, ohne Messwerte zu verlieren, und sorgt dafür, dass Messwerte von vor dem Stellen der Uhr zur richtigen Zeit gespeichert werden.

Erst speichern, dann senden

Schreib jeden Messwert zuerst in einen lokalen Puffer, zum Beispiel SQLite, und sende von dort: die ältesten zuerst, in Stapeln von höchstens 1000. So wartet das Messen nie auf das Netzwerk.

Antwort Was tun
200 Lösche den Stapel aus dem Puffer, außer den abgelehnten Messwerten mit retry: true. Die kommen in einen späteren Stapel
Keine Antwort, ein Timeout, 429 oder 5xx Behalte den Stapel und sende ihn später erneut, mit derselben batch_uuid. Was der Server schon hatte, zählt als duplicates
422 Der Stapel ist als Ganzes ungültig, und erneut senden hilft nicht. Leg ihn beiseite, damit er den Rest nicht blockiert, und such den Fehler
401 Behalte alles. Mit einem neuen Token kannst du noch alles senden
  • Ein 200, das nicht von der API kommt, etwa die Anmeldeseite eines Hotspots, bestätigt nichts. Prüfe, ob die Antwort JSON mit accepted ist.
  • Puffer voll? Verwirf die ältesten Messwerte, nicht die neuesten. Zum Vergleich: Die BoilerBuddy Alpo behält höchstens eine Million, etwas mehr als zwei Monate bei neun Sensoren pro Minute.

Nach einem Ausfall aufholen

Pro Token darfst du 10 Stapel mit je 1000 Messwerten pro Minute hochladen. Eine Woche ohne Internet mit 8 Sensoren, die jede Minute messen, sind 8 × 1440 × 7 = 80.640 Messwerte: etwa neun Minuten Aufholen bei voller Geschwindigkeit.

  • Lass innerhalb deiner 60 Anfragen pro Minute Platz für config und status. Höchstens 8 Stapel mit je 500 pro Minute ist ein sicheres Tempo.
  • Messwerte, die älter als 365 Tage sind, bekommen measured_at_too_old.
  • Ein Messwert, der im Stapel auf einen achten Kalendertag (UTC) fällt, bekommt retry: true und kommt in den nächsten. Sendest du der Reihe nach, erreichst du das nicht.
  • Mehr als 5 Minuten in der Zukunft wird abgelehnt. Eine Uhr, die vorgeht, kostet dich Messwerte: Halte sie mit NTP synchron.

Die Uhr

Selbstgebaute Hardware wie ein Raspberry Pi oder ESP32 hat meist keine batteriegepufferte Uhr. Nach dem Einschalten stimmt die Zeit nicht (1970 oder der Moment des Ausschaltens), bis NTP sie stellt. Du kannst:

  1. Mit dem Messen warten, bis die Uhr gestellt ist. Am wenigsten Arbeit: Dir fehlt nur etwa die erste Minute. Das ESP32-Beispiel macht es so.
  2. Trotzdem messen und den Server korrigieren lassen, mit clock_synced: false und der monotonen Uhr. Siehe unten.
  3. Den Date-Header einer beliebigen Serverantwort als Zeitquelle für den Uhr-Anker nutzen, solange NTP noch nicht synchronisiert hat.

So funktioniert die Korrektur

Gib bei jedem Messwert, der vor dem Stellen der Uhr aufgenommen wurde, Folgendes mit:

  • clock_synced: false. Ein echtes false: "false" oder 0 ergibt invalid_item.
  • boot_id: eine ID für diesen Startvorgang. Unter Linux steht sie in /proc/sys/kernel/random/boot_id; auf einem Mikrocontroller erzeugst du bei jedem Start eine zufällige.
  • mono_ns: die monotone Uhr in Nanosekunden zum Zeitpunkt der Messung. Sie zählt ab dem Start und springt nie.
  • measured_at ist weiterhin Pflicht und muss ein gültiger Zeitstempel sein. Sende, was deine Uhr anzeigt.

Der Stapel trägt dann einen Uhr-Anker: die Uhr zum Zeitpunkt des Sendens.

"clock": {"boot_id": "4b8e7c1d-…", "synced": true, "sent_at": "2026-10-15T09:31:02Z", "sent_mono_ns": 72000000000}

Der Server berechnet measured_at = sent_at − (sent_mono_ns − mono_ns). Das funktioniert nur, wenn der Anker synced: true hat, die boot_id des Messwerts der des Ankers entspricht und mono_ns eine ganze Zahl ist, die nicht größer als sent_mono_ns ist. Danach gelten die üblichen Limits: höchstens 5 Minuten in der Zukunft, nicht älter als 365 Tage.

Klappt das nicht, bekommt der Messwert clock_unsynced:

  • Kein Anker, oder ein Anker mit synced: false: retry: true. Sende den Messwert erneut, sobald deine Uhr gestellt ist. Solange das Gerät nicht neu gestartet ist, kann der Server ihn noch einordnen.
  • Ein synchronisierter Anker, aber eine andere boot_id oder kein brauchbares mono_ns: retry: false. Der Messwert stammt aus einem früheren Start und lässt sich nie einordnen. Sende nicht synchronisierte Messwerte also bald nach dem Stellen der Uhr, vor einem Neustart.

Ein Beispiel

Ein Gerät startet mit seiner Uhr auf 1970. Nach 12 Sekunden misst es T1: 48,2 °C, mit der Uhrzeit 1970-01-01T00:00:12Z und der monotonen Uhr bei 12.000.000.000 ns. Nach 40 Sekunden stellt NTP die Uhr, und nach 72 Sekunden geht der Stapel raus:

{
  "clock": {"boot_id": "4b8e7c1d-2f3a-4c5b-8d6e-9f0a1b2c3d4e", "synced": true,
            "sent_at": "2026-10-15T09:31:02Z", "sent_mono_ns": 72000000000},
  "readings": [
    {"sensor": "T1", "value": 48.2, "measured_at": "1970-01-01T00:00:12Z",
     "clock_synced": false, "boot_id": "4b8e7c1d-2f3a-4c5b-8d6e-9f0a1b2c3d4e",
     "mono_ns": 12000000000}
  ]
}

Die Differenz ist 72 − 12 = 60 Sekunden, also wird der Messwert unter 09:31:02 − 60 s = 2026-10-15T09:30:02Z gespeichert.

Wäre der Stapel nach 30 Sekunden rausgegangen, mit einem Anker mit synced: false, hätte die Antwort {"index": 0, "reason": "clock_unsynced", "retry": true} gelautet. Das Gerät behält den Messwert dann, und nach 72 Sekunden geht er durch.

Kommst du nicht weiter?

Stell deine Frage über das Kontaktformular. Wir helfen dir weiter und verbessern diese Seite.

Kontakt aufnehmen