Sensor-API
Sende Messwerte von jedem Gerät, das einen HTTP-Request mit HMAC-SHA-256 signieren kann. Vier Haupt-Sensortypen — Bodenfeuchte, Temperatur, Luftfeuchte, Luftdruck — plus erweiterte Typen für Licht, pH, EC und CO₂.
Die gleiche Anleitung findest du in der App unter Profil → Mehr → Sensoren.
Sensor-API
Beet Lotse nimmt Messwerte von jedem Gerät an, das eine HTTP-Anfrage mit HMAC-SHA-256 signieren kann – etwa ein ESP32, ein Raspberry Pi oder ein kleines Skript.
Kurzfassung
POST https://beetlotse.de/api/v1/sensors/{id}/readings
Header: X-Sensor-Id, X-Sensor-Secret, X-Sensor-Signature
Body: {"batch_id":"...", "readings":[{"value":..,"unit":"..","recorded_at":"..."}]}
Signatur: HMAC_SHA256(secret, rohe_body_bytes), hex
Drei Schritte: Sensor in der App anlegen → ID und Schlüssel aufs Gerät übertragen → Messwerte senden.
1. Sensortypen
Haupttypen
type |
Misst | Einheit (metrisch) | Imperial | Bereich |
|---|---|---|---|---|
soil_moisture |
Bodenfeuchte | "%" (0–100) |
— | 0 bis 100 |
temperature |
Luft-/Bodentemperatur | "°C" |
"°F" |
−40 bis 85 |
humidity |
Relative Luftfeuchte | "%" (0–100) |
— | 0 bis 100 |
barometric_pressure |
Luftdruck | "hPa" (= mbar) |
"inHg" |
870 bis 1085 |
Der Server speichert value und unit so, wie sie ankommen. Die
Apps rechnen Einheiten für die Anzeige um.
Bodenfeuchte: Prozent oder Rohwert
Kapazitive Bodenfeuchte-Sensoren liefern von sich aus keine Prozente, sondern einen Rohwert (meist 200–800 bei einem 12-Bit-ADC). Sende das, was dein Gerät hat:
| Modus | unit |
Wann |
|---|---|---|
| Prozent | "%" |
Das Gerät kennt seine Trocken-/Nass-Werte bereits |
| Rohwert | "raw" |
Kapazitiver Sensor ohne Kalibrierung auf dem Gerät |
Im Rohwert-Modus kalibrierst du einmal in der App: Sensor an der Luft → „Trockenwert speichern“; Sensor in einem Glas Wasser → „Nasswert speichern“. Danach zeigt die App Prozent an:
pct = clamp((raw - value_dry) / (value_wet - value_dry) * 100, 0, 100)
Rohwerte bleiben gespeichert, du kannst also später neu kalibrieren, ohne den Verlauf zu verlieren. Automatische Gieß-Erinnerungen nutzen die kalibrierten Prozent.
Weitere Typen
type |
Einheit | Hinweis |
|---|---|---|
light |
"lux" |
Helligkeit |
ph |
"pH" |
0–14 |
ec |
"mS/cm" |
Leitfähigkeit der Nährlösung |
co2 |
"ppm" |
CO₂-Konzentration |
custom |
bis 20 Zeichen | Freie Einheit |
2. Voraussetzungen
- Bestätigte E-Mail-Adresse.
- Pro-Tarif, um Sensoren anzulegen (sonst
402). Sensoren, die du vor einem Wechsel zu Free angelegt hast, kannst du weiter auslesen.
3. Sensor anlegen
In der App (empfohlen)
Profil → Mehr → Sensoren → Sensor hinzufügen. Wähle, was der Sensor misst, gib ihm einen Namen und ordne ihn einem Tray zu. Danach zeigt die App Sensor-ID und geheimen Schlüssel – genau einmal. Übertrage beides auf dein Gerät, bevor du den Dialog schließt.
Über die API
Token holen:
TOKEN=$(curl -sS -X POST https://beetlotse.de/api/v1/auth/login \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=<DEINE_EMAIL>' \
--data-urlencode 'password=<DEIN_PASSWORT>' \
| python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
Bodenfeuchte-Sensor für Tray 3 anlegen:
curl -sS -X POST https://beetlotse.de/api/v1/sensors \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Hochbeet A", "type": "soil_moisture", "tray_id": 3}'
Die Antwort enthält den Schlüssel – das einzige Mal, dass du ihn siehst:
{
"id": 12,
"name": "Hochbeet A",
"type": "soil_moisture",
"tray_id": 3,
"secret": "<wird einmal angezeigt>"
}
4. Messwerte senden
Jeder POST ist ein Paket mit bis zu 2.000 Messwerten, signiert
mit dem Schlüssel des Sensors. Doppelte Werte werden über
(sensor_id, batch_id, recorded_at) erkannt; ein Paket erneut zu
senden ist also unschädlich.
Header
| Header | Wert |
|---|---|
X-Sensor-Id |
Die Sensor-ID (wie im Pfad) |
X-Sensor-Secret |
Der Schlüssel aus der Anlage |
X-Sensor-Signature |
HMAC_SHA256(secret, rohe_body_bytes) hex |
Content-Type |
application/json |
Signiere genau die Bytes, die du sendest – das JSON nach dem Signieren nicht neu serialisieren.
Body
{
"batch_id": "eine-eindeutige-id-vom-geraet",
"readings": [
{ "value": 36.2, "unit": "%", "recorded_at": "2026-05-29T17:00:00Z" },
{ "value": 421, "unit": "raw", "recorded_at": "2026-05-29T17:05:00Z" }
]
}
Beispiele je Typ:
// soil_moisture (Prozent)
{ "value": 36.2, "unit": "%", "recorded_at": "2026-05-29T17:00:00Z" }
// soil_moisture (Rohwert – in der App kalibrieren)
{ "value": 421, "unit": "raw", "recorded_at": "2026-05-29T17:00:00Z" }
// temperature
{ "value": 21.4, "unit": "°C", "recorded_at": "2026-05-29T17:00:00Z" }
// humidity
{ "value": 58.0, "unit": "%", "recorded_at": "2026-05-29T17:00:00Z" }
// barometric_pressure
{ "value": 1013.2, "unit": "hPa", "recorded_at": "2026-05-29T17:00:00Z" }
Vollständiges curl-Beispiel
SENSOR_ID=<DEINE_SENSOR_ID>
SECRET="<DEIN_SENSOR_SCHLUESSEL>"
BODY='{"batch_id":"demo-01","readings":[{"value":36.2,"unit":"%","recorded_at":"2026-05-29T17:00:00Z"}]}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -sS -X POST "https://beetlotse.de/api/v1/sensors/$SENSOR_ID/readings" \
-H "X-Sensor-Id: $SENSOR_ID" \
-H "X-Sensor-Secret: $SECRET" \
-H "X-Sensor-Signature: $SIG" \
-H "Content-Type: application/json" \
-d "$BODY"
Antwort:
{ "batch_id": "demo-01", "received": 1, "inserted": 1, "deduped": 0 }
Fehler
| HTTP | Ursache | Lösung |
|---|---|---|
| 400 | X-Sensor-Id passt nicht zum Pfad |
An beiden Stellen dieselbe ID verwenden |
| 401 | Falscher Schlüssel | Sensor neu anlegen; der Schlüssel wird nur einmal angezeigt |
| 401 | Falsche Signatur | Genau die gesendeten Bytes signieren |
| 402 | Kein Pro-Tarif | Siehe Abschnitt 2 |
| 403 | E-Mail nicht bestätigt | E-Mail-Adresse bestätigen |
| 404 | Sensor wurde gelöscht | Sensor neu anlegen |
| 410 | Sensor deaktiviert | Neuen Sensor anlegen |
| 422 | Ungültiger Body | unit max. 20 Zeichen; recorded_at ISO-8601 mit Zeitzone |
5. Beispiel in Python
Läuft auf einem Raspberry Pi; mit MicroPython funktioniert es genauso
mit urequests und hmac:
import json, hmac, hashlib, time, requests, datetime as dt
API = "https://beetlotse.de/api/v1"
SENSOR_ID = 12 # deine Sensor-ID
SECRET = "<DEIN_SENSOR_SCHLUESSEL>"
def now():
return dt.datetime.now(dt.timezone.utc).isoformat()
def send_batch(readings, batch_id=None):
batch_id = batch_id or f"py-{int(time.time())}"
body = json.dumps({"batch_id": batch_id, "readings": readings}).encode()
sig = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
r = requests.post(
f"{API}/sensors/{SENSOR_ID}/readings",
headers={
"X-Sensor-Id": str(SENSOR_ID),
"X-Sensor-Secret": SECRET,
"X-Sensor-Signature": sig,
"Content-Type": "application/json",
},
data=body, # rohe Bytes, nicht das Dict
timeout=10,
)
r.raise_for_status()
return r.json()
# Bodenfeuchte in Prozent
print(send_batch([{"value": 36.2, "unit": "%", "recorded_at": now()}]))
Halte die unit passend zum Sensortyp, damit die Diagramme stimmen.
6. Messwerte abrufen
curl -sS "https://beetlotse.de/api/v1/sensors/12/readings?limit=50" \
-H "Authorization: Bearer $TOKEN"
Zeitraum: ?from=2026-05-01T00:00:00Z&to=2026-06-01T00:00:00Z
(ISO-8601 mit Zeitzone; höchstens 10.000 Werte pro Antwort).
7. Sicherheit
- Der Schlüssel wird nur einmal angezeigt. Wir speichern nur einen Hash davon; wenn du ihn verlierst, lege den Sensor neu an.
- Immer über HTTPS senden; unverschlüsseltes HTTP wird abgelehnt.
- Schlüssel verloren oder weitergegeben? Lösche den Sensor in der
App. Das Gerät bekommt beim nächsten Paket
410 Gone. - Limit pro Sensor: 20 Pakete pro Minute.
8. Sensor, Tray und Standort
Ein Sensor gehört zu deinem Konto und kann einem Tray (z. B. ein Bodenfeuchte-Sensor im Hochbeet) oder einem Standort (z. B. ein Temperatursensor fürs ganze Gewächshaus) zugeordnet werden. Die Zuordnung änderst du in der App unter Profil → Mehr → Sensoren → Sensor auswählen.
9. Diese Anleitung in der App
Du findest diese Anleitung in der App unter Profil → Mehr → Sensoren. „Per E-Mail senden“ schickt sie dir zusammen mit der Liste deiner Sensor-IDs – praktisch, wenn du ein Gerät abseits vom Handy einrichtest.
Bereit, einen Sensor anzubinden? Preise ansehen oder Kontakt aufnehmen.