Daten aus der Natede-API auslesen

Wie wir “in” Apps schauen und die entsprechenden Requests auslesen können, haben wir uns ja bereits angeschaut. In diesem Artikel soll es nun darum gehen, wie wir Daten aus der Natede-API auslesen können, um beispielsweise unseren Home Assistant (oder FHEM, whatever) damit zu füttern.

Vorab, was ist Natede überhaupt? Natede ist ein “smarter Blumentopf”, der die heimische Luft von Schadstoffen befreien möchte. Um dies zu tun hat er einige Sensoren verbaut, die dann – aktuell und offiziell leider nur über die App – auslesbar sind. Zu diesen Sensordaten zählen Temperatur und Luftfeuchtigkeit, VOCs, CO und Feinstaub (PM2,5).

Vitesy setzt hier anscheinend auf Firebase, die Doku zur Authentifizierung gibt’s hier.

Den refresh_token, den ich in folgendem Request mitschicke, finde ich in den mitmproxy-“Flows”. Mit diesem kann ich mir meinem Token für die API holen.

curl -H "Content-Type: application/json" --data-binary '{"refresh_token":"XXX"}' -X POST "https://securetoken.googleapis.com/v1/token?grant_type=refresh_token&key=AIzaSyD45X1yTlDyZIZxy6btd9Pefcv5JHg9kek" 2&>1 | jq .
{
  "access_token": "eyJhbGciOixxxxxxxxxxxxxxx",
  "expires_in": "3600",
  "token_type": "Bearer",
  "refresh_token": "XXX",
  "id_token": "eyJhbGciOixxxxxxxxxxxxxxx",
  "user_id": "7XVxxxxxxxxxxxxxx",
  "project_id": "860446277852"
}

Um sich den refresh_token direkt mittels E-Mail und Passwort zu holen kann folgendes cURL-Kommando genutzt werden:

curl -H "Content-Type: application/json" --data-binary '{"email":"[email protected]","password":"einPa$$wort","returnSecureToken":true}' -X POST "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?&key=AIzaSyD45X1yTlDyZIZxy6btd9Pefcv5JHg9kek" 2&>1 | jq .
{
  "kind": "identitytoolkit#VerifyPasswordResponse",
  "localId": "7XVxxxxxxxxxxxxxx",
  "email": "[email protected]",
  "displayName": "John Doe",
  "idToken": "eyJhbGciOixxxxxxxxxxxxxxx",
  "registered": true,
  "profilePicture": "https://graph.facebook.com/xxx/picture",
  "refreshToken": "AEu4Ixxxxxxxxxxx",
  "expiresIn": "3600"
}

Interessant für uns hier in dem Fall ist der id_token (JWT), den wir später für unsere Requests an die Natede-API (https://api.clairyhub.com/; Clairy ist btw der Vorgänger, wobei die APIs hier aber grundlegend verschieden sind) brauchen. Den aktuellen idToken, der aber nur 1 Stunde gültig ist, finden wir auch in unseren Requests:

Um nun beispielsweise unsere Geräte auszugeben, reicht folgender Request (wobei ich hier einige Werte der Response anonymisiert habe):

curl --request GET 'https://api.clairyhub.com/users/me/devices' \
--header "Authorization: Bearer ${id_token}" 2&>1 | jq .
[
  {
    "fwVersion": "1.7.08",
    "location": {
      "zipcode": "86xxx",
      "country": "Deutschland",
      "utcOffset": 60,
      "formattedAddress": "Strasse 1, 86xxx Ort, Deutschland",
      "city": "Ort",
      "street": "Strasse, 1",
      "latitude": 0.0,
      "name": "Strasse 1",
      "placeId": "xxx",
      "description": "Strasse 1, 86xxx Ort, Deutschland",
      "longitude": 0.0
    },
    "isEnrollCompleted": true,
    "entityId": "E8:2A:44:00:00:00",
    "createdAt": "2019-12-20T14:40:00+00:00",
    "connected": true,
    "isEnrollSucceeded": true,
    "deviceName": "NATEDE Wohnzimmer",
    "updatedAt": "2020-01-24T09:00:30+00:00",
    "ssid": "FRITZ!Box",
    "certificateId": "xxx",
    "plant": {
      "id": "spathiphyllum",
      "name": "CLPLANT-spathiphyllum"
    },
    "place": {
      "id": "home",
      "name": "Home",
      "room": {
        "id": "living",
        "name": "Living"
      }
    },
    "sensors": [
      "tmp",
      "hum",
      "vocL",
      "dust",
      "co"
    ],
    "premium": false
  }
]

Anhand der entityId wird unser Gerät identifiziert, dieser entspricht der MAC-Adresse. Möchten wir nun die letzten Messwerte abrufen, hilft folgender cURL-Request:

curl --request GET 'https://api.clairyhub.com/devices/E8:2A:44:00:00:00/measurements/latest' \
--header "Authorization: Bearer ${id_token}" 2&>1 | jq .
{
  "uv": 0,
  "fan": 0,
  "pm25": 10.2,
  "deviceId": "E8:2A:44:00:00:00",
  "tmp": 24.4,
  "vocL": 1,
  "timestamp": 1579878607,
  "hum": 22,
  "co": 0,
  "measuredAt": "2020-01-24T16:10:07+01:00"
}

So weit, so gut. Die aktuellen Messwerte werden aktuell alle 10 Minuten geschickt. Um jetzt alle Messwerte für einen bestimmten Zeitraum zu bekommen, in folgendem Beispiel 1 Tag (Response gekürzt):

curl --request GET 'https://api.clairyhub.com/measurements?deviceId=E8%3A2A%3A44%3A00%3A00%3A00&fromDateTime=2020-01-23T00%3A00%2B01%3A00&toDateTime=2020-01-23T23%3A59%3A59.999999999%2B01%3A00&granularity=finest&sensors=tmp%2Chum%2Cpm25%2Cco%2Cco2%2Cvoc%2CvocL' \
--header "Authorization: Bearer ${id_token}" 2&>1 | jq .
[
  {
    "deviceId": "E8:2A:44:00:00:00",
    "firstMeasurementAt": "2020-01-23T00:06:07+01:00",
    "lastMeasurementAt": "2020-01-23T00:06:07+01:00",
    "aqi": {
      "count": 1,
      "max": 53.17297525495789,
      "min": 53.17297525495789,
      "sum": 53.17297525495789
    },
    "co": {
      "count": 1,
      "max": 0,
      "min": 0,
      "sum": 0
    },
    "hum": {
      "count": 1,
      "max": 33,
      "min": 33,
      "sum": 33
    },
    "pm25": {
      "count": 1,
      "max": 38.1,
      "min": 38.1,
      "sum": 38.1
    },
    "tmp": {
      "count": 1,
      "max": 13.1,
      "min": 13.1,
      "sum": 13.1
    },
    "vocL": {
      "count": 1,
      "max": 1,
      "min": 1,
      "sum": 1
    }
  },
  {
    "deviceId": "E8:2A:44:00:00:00",
    "firstMeasurementAt": "2020-01-23T00:16:08+01:00",
    "lastMeasurementAt": "2020-01-23T00:16:08+01:00",
    "aqi": {
      "count": 1,
      "max": 51.32705573108122,
      "min": 51.32705573108122,
      "sum": 51.32705573108122
    },
    "co": {
      "count": 1,
      "max": 0,
      "min": 0,
      "sum": 0
    },
    "hum": {
      "count": 1,
      "max": 33,
      "min": 33,
      "sum": 33
    },
    "pm25": {
      "count": 1,
      "max": 40.2,
      "min": 40.2,
      "sum": 40.2
    },
    "tmp": {
      "count": 1,
      "max": 13.1,
      "min": 13.1,
      "sum": 13.1
    },
    "vocL": {
      "count": 1,
      "max": 1,
      "min": 1,
      "sum": 1
    }
  },
  ...
]

Möchten wir nur bestimmte Sensor-Werte, können wir den Parameter sensors auch entsprechend anpassen.

Ähnliches Vorgehen, um die Alarme zu bekommen:

https://api.clairyhub.com/devices/E8:2A:44:00:00:00/alerts?fromDate=2020-01-23T00%3A00%2B01%3A00&toDate=2020-01-23T23%3A59%3A59.999999999%2B01%3A00

Wobei die Parameter fromDate und toDate hier optional sind.

Auf GitHub habe ich mal angefangen, da ein bisschen was zusammen zu tragen… :)

Möchtest Du dazu etwas sagen?