In meinem Streben nach einem offenen Smarthome, inklusive selbstgebauter HomeKit-Accessories, hatte ich zuletzt einen neuen Einfall: Nachdem ich mit der OSAMD-Messstation eine eigene Luftqualitätsmesstation bereits baute, wollte ich einmal in die Vorzüge einer kommerziellen Messstation kommen: Kompaktheit und fülle an Sensoren in schönen Design. Nach etwas Recherche entschied ich mich für den Marktführer Qingping, welche mit ihrem Air Monitor Lite eine kleine, kompakte Box mit OLED-Panel, Feinstaub-Sensor, Luftfeuchte- und Lufttemperatursensor sowie Luftqualitätsindexsensor bereitstellten. Im Angebot für 65 Euro eine kompakte, rundum fähige Box.
Und so begab ich mich einmal mehr auf die Reise des „kommerzielle Produkte kaufen, eigene Datenkollektion anflanschen“.
An die Daten kommen
Am ersten Punkt jedes solchen Projektes steht die Grundvoraussetzung, an die Daten des Geräts zu kommen. Eigentlich sollte dies auch nicht so schwer sein: Qingping stellt eine API nach OpenAPI-Spezifikation bereit. Also sollte alles in 15 Minuten laufen? Falsch gedacht!
Allein das Regisitrieren des Gerätes brachte erste Schwierigkeiten mit sich: In der unglaublich instabilen und bugreichen App „Qingping+“ werden Homekit-Geräte zwar erkannt und ausgelesen, aber nicht dem Qingping-Account korrekt hinzugefügt. Es stellt sich heraus, dass die Geräte erst nach reset (oben auf die Funktionsleiste des Sensors circa 10 Sekunden drücken) und rekofigurieren als Bluetooth-Bridge aus der App (dann ohne Homekit) im Account hinterlegt werden. Das auch ohne diese Konfiguration teilweise der Sensor in der Account-Übersicht innerhalb der App angezeigt werden, lässt sich nur als Bug werten. Daten kommen ohne diesen Schritt jedenfalls nicht (offiziell) bei Qingping an. Ob das hinzufügen zum heimischen WLAN klappt, steht bei diesem Weg auch in den Sternen. Bei mir wurde im lokalen IOT-WLAN das Passwort nicht angenommen, nur im Gastzugang konnte ich das Gerät korrekt hinzufügen (beide ausschließlich 2,5 GHz-WLANs).
Als nächstes muss man sich mit seinem Qingping-Account bei https://developer.qingping.co/ einloggen. Ob das Gerät richtig hinterlegt wurde, lässt sich bei „Private Access Config“ nachschauen, wenn man ein Gerät hinzufügen möchte (Devices in der Sidebar links, dann „add Device). Wenn alles mit dem Hinzufügen geklappt hat, sollte dort das Gerät angezeigt werden:
Und nun war es damit an der Zeit, an die Daten des Gerätes zu kommen: In der Entwickerdokumentation wurde nur sehr lückenhaft über die erwarteten Daten berichtet, nach einiger Zeit konnte ich jedoch herausfinden, dass zuerst über die API nach OAuth-Spec ein Token erstellt werden müsste, welcher dann wiederrum alle zwei Stunden ablaufen würde und regeneriert werden musste. Ich überspringe nun mal die vielzahl der Fehlversuche, die Anfrage richtig zu formen und komme direkt zu der funktionierenden Form:
curl -u APPKEY:APPSECRET -H "Content-Type: application/x-www-form-urlencoded" -X POST https://oauth.cleargrass.com/oauth2/token -d "grant_type=client_credentials&scope=device_full_access"
gibt dabei das folgende zurück:
{"access_token":"TOKEN-MIT-87_CHARACTEREN","expires_in":7199,"scope":"device_full_access","token_type":"bearer"}
APPKEY beziehungsweise APPSECRET finden sich dabei durch einen Klick auf die E-Mail-Adresse oben rechts im Developer-Portal unter „Access Managment“.
Nun kann Testweise eine Datenabfrage an die API mit dem neugewonnenen Accesstoken erfolgen:
curl -H "Authorization: Bearer DEIN_TOKEN" -H "Content-Type: application/json" -X GET https://apis.cleargrass.com/v1/apis/devices
sollte eine Antwort ähnlich zu dieser wiedergeben:
Es fällt dabei schnell auf, dass „report_interval“:900,“collect_interval“:900 nur jeweils alle 900 Sekunden, also alle 15 Minuten, die Daten des Sensors abgefragt werden. Dies können wir mit einer passenden Payload an die Settings-API anpassen:
curl -H "Authorization: Bearer DEIN_TOKEN" -H "Content-Type: application/json" -X GET https://apis.cleargrass.com/v1/apis/devices/settings --json '{"mac": ["GERÄT_MACADRESSE"], "report_interval": 10, "collect_interval": 5, "timestamp": 1716658625}'
Damit sollte dann im Report-Intervall und Collect-Intervall ein anderer Wert fortan angezeigt werden. Der Timestamp sollte dabei jeweils innerhalb eines 20-Sekündigen Zeitfensters aktuell sein – eine Webseite wie https://www.unixtimestamp.com/ gibt diesen gern aus.
Python-Implementierung
Mit dieser Basis als Unterbau war es nun mehr an der Zeit, das ganze in ein Python-Programm zu implementieren. Hier hatte ich von dem Projekt Meross2InfluxDB bereits eine grobe Struktur sowie Build-Scripte noch auf Lager. Da mit Qingping jedoch nicht über eine eigene Python-Library kommuniziert wird, sondern ein GET-Request an die API geschickt wird, musste doch der Unterbau von Meross2InfluxDB angefasst werden: Weg mit der Geräteauswahl, her mit API-Keyrolling.
Nach rund drei Stunden hin-und-her bei der Entwicklung hatte ich schließlich ein Python-Programm mit den folgenden Features:
- Asynchrone Funktionen zum Abholen der API-Schlüssel: Jede Stunde wird ein neuer API-Schlüssel besorgt, dies passiert in einer asynchron-laufenden Funktion zu der Data-Fetch-Funktion
- Automatisches Einstellen des Refresh-Intervalls des Sensors in eigener Subroutine.
- Flexible Datenpunkterstellung: Qingping2InfluxDB sucht nicht nach spezifischen (Mess-)werten, sondern schreibt volle Key-Value-Pairs des Datenblocks der API-Antwort in die InfluxDB (ergo: Auch andere Qingping-Messgeräte sollten sich ohne weitere Konfiguration auslesen lassen).
- Health-Check der Influx-DB und der Qingping-API mit Reporting an Docker.
Auch hier lässt sich wieder der gesamte Code mit allem drum- und dran auf GitHub nachschauen.
Docker-Image
Auch bei diesem Projekt habe ich wieder eine Docker-Image gebaut und auf dem Docker Hub zum Download zur Verfügung gestellt.
Eine Docker-Compose-Datei könnte so etwa aussehen:
Damit sollte das Projekt auch schon lauffähig sein und die Daten in InfluxDB werfen. Probleme mit dem Programm bitte wie immer als GitHub-Issue anstatt als Kommentar auf dieser Seite hinterlassen. Danke!
Update, 11.06.2024
Es hat sich gezeigt, dass es Sinnvoll ist, für den dauerhaften Docker-Betrieb einmal am Tag den Container neu zu starten, zum Beispiel per Cronjob:
crontab -e
0 0 * * * cd /pfad/zur/docker-compose.yml && /usr/bin/docker compose down && /usr/bin/docker compose up -d
Woher dieses Problem kommt, versuche ich aktuell herauszufinden. Bis dahin ist ein solcher Cronjob nur sinnvoll.