Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push to online account #12

Open
nakitadog opened this issue Sep 18, 2020 · 15 comments
Open

Push to online account #12

nakitadog opened this issue Sep 18, 2020 · 15 comments

Comments

@nakitadog
Copy link

Would it be possible to use this script to push to the online account instead of needing to use the phone app?

@Einstein2150
Copy link

Einstein2150 commented Jan 7, 2022

It's maybe not the goal you want to reach but you can send the data to any smart-home-system of your choice. My wave arrived yesterday. This is what I made so far:

The Raspberry Pi which controls my 3D-printer runs the script with cron every half hour and gets the bluetooth-data from the device. After that it makes a POST with cURL to Node Red. I changed the script a bit. It is sending the Output now in JSON-format:

def str(self):
msg = f"\u007B "Humidity" : {self.humidity}, "Temperature" : {self.temperature}, "Radon STA" : {self.radon_sta}, "Radon LTA" : {self.radon_lta} \u007D "
return msg

Update: I send the data now direct out of the python-script without pipe it to cURL. --> https://github.com/Einstein2150/wave-reader

In Node Red I can log the data or visualize it in a dashboard.

@LordEvron
Copy link

I am also interested to this feature. Apparently their API does not support POST new data, but you can only read them.

https://developer.airthings.com/consumer-api-docs/

Did anyone found a way around this? I really would like to keep my device sync all the time!

Maybe someone from Airthings could comment on it?

@nakitadog
Copy link
Author

I did the following:

  1. I wrote some code that interacts with the API. It simply checks each day to see when the Wave was last synced. If it's been more than 24 hours, I alert myself via email that the Wave is out of sync.

  2. I also use a spare android device whose only purpose is to sync with the Wave. I then use MacroDroid to make the Wave app sync with the cloud account on a regular basis.

All this wouldn't be necessary if there was a desktop app but it seems that they want you to purchase an Airthings Hub to accomplish this simple task of regularly syncing with the cloud account.

@Einstein2150
Copy link

intercepting the traffic between the app and the server would be good 😁
I will try it in the next days.

@Einstein2150
Copy link

So here is the traffic step by step:

First it refreshes the token from https://api.airthin.gs/v1/refresh
The app posts the current token:
{ "refreshToken": "[token]" }

The server responses with a confirmation:
{ "accessToken": "[token]", "expiresIn": 10800, "idToken": "[token]", "now": "2022-01-11T06:36:05", "refreshToken": "[token]" }

This happens 2 times...

Now the app requests from https://api.airthin.gs/v1/me?includeHubs=true with authorization = [token] and a x-api-key = [myapikey] a cloud status report:

{ "devices": [ { "batteryPercentage": 100, "currentSensorValues": [ { "isAlert": false, "preferredUnit": "bq", "providedUnit": "bq", "thresholds": [ 100, 150 ], "type": "radonShortTermAvg", "value": 70.0 }, { "isAlert": false, "preferredUnit": "c", "providedUnit": "c", "thresholds": [ 18, 25 ], "type": "temp", "value": 20.2 }, { "isAlert": false, "preferredUnit": "pct", "providedUnit": "pct", "thresholds": [ 25, 30, 60, 70 ], "type": "humidity", "value": 42.0 } ], "lat": 47.763874, "latestSample": "2022-01-10T05:43:00", "lng": 12.457217, "locationId": "090daab0-bd3d-4c4a-8e2f-8f0074xxxxxx", "locationName": "Arbeitsplatz", "relayDevice": "APP", "roomName": "Keller", "segmentId": "7ef25646-299a-432a-ba06-033231xxxxxx", "segmentStart": "2022-01-05T10:31:12", "serialNumber": "2950xxxxxx", "signalQuality": "NO_SIGNAL", "type": "wave2" } ], "email": "xxxxxx", "enabledToggles": [ "mold_enabled", "notifications_enabled", "pollen_link_enabled", "albatross_enabled" ], "name": "Raik Schneider", "preferences": { "androidIntercomUserHash": "cfdd497173d89bb4b0deea77a71d80b4b1c2266579c665877d66d181b1xxxxxx", "dateFormat": "EUR", "hubMode": "NO_HUB", "iosIntercomUserHash": "74dcf43d8a51ddccca92105f48036328259b976da6c3ed3b70b6db41faxxxxxx", "language": "de", "measurementUnits": "METRIC", "proUser": false, "radonUnit": "bq", "tempUnit": "c", "userId": "e8949c08-7a33-4b75-b6ea-fe267fxxxxxx" } }

Look at the "latestSample": "2022-01-10T05:43:00"

Now it checks the data before starting an upload of the new values since the latest sample in the cloud.

https://api.airthin.gs/v1/me/devices/2950xxxxxx/segments/latest/samples?from=2022-01-10T05%3A43%3A00&includeIds=true&to=2022-01-11T06%3A36%3A06 with authorization = [token] and a x-api-key = [myapikey]

The app gets the latest cloud data:

{ "idsForOffsets": [ [ 13210183611279476043 ] ], "lastRecord": "2022-01-10T05:43:00", "lat": xx, "lng": xx, "location": "Arbeitsplatz", "moreDataAvailable": false, "nextPageStart": "2022-01-10T05:43:01", "offsets": [ [ 414708 ] ], "room": "Keller", "segmentId": "7ef25646-299a-432a-ba06-033231xxxxxx", "segmentName": "Keller", "segmentStart": "2022-01-05T10:31:12", "sensors": [ { "offsetType": 0, "type": "radonShortTermAvg", "values": [ 70.0 ] }, { "offsetType": 0, "type": "temp", "values": [ 20.19 ] }, { "offsetType": 0, "type": "humidity", "values": [ 41.5 ] } ] }

Now it posts the new samples to https://api.airthin.gs/v1/me/devices/2950037978/segments/latest/samples with authorization = [token] and a x-api-key = [myapikey]

{ "installationId": "61175029-3349-4BA7-953D-A5958Axxxxxx", "macAddress": "D8:71:4D:xx:xx:xx", "macAddressWrittenDate": 1641882971, "samples": [ { "accel": 0, "accelEvent": false, "appState": "active", "appVersion": "3.8.7(426)", "battCharge": null, "battVoltage": 3063, "bleConnected": null, "co2": null, "cycle": 1, "debug": null, "errorFlag": null, "handWaves": 0, "humidity": 41.5, "id": 13210183611279476044, "light": 0, "pressure": null, "radonInstant": null, "radonLongTermAvg": null, "radonShortTermAvg": null, "record": 1356, "relayDevice": "iPhone", "relayDeviceOS": "iOS,15.3", "sampleRecorded": "2022-01-10T05:48:00", "sampleTransferred": "2022-01-11T06:36:12", "submitted": "2022-01-11T06:36:12", "temp": 20.200000762939453, "voc": null, "waveCcFwVersion": "1.5.3", "waveMspFwVersion": "1.6.0", "waveSub1FwVersion": "2.0.2" }, { "accel": 0, "accelEvent": false, "appState": "active", "appVersion": "3.8.7(426)", "battCharge": null, "battVoltage": 3063, "bleConnected": null, "co2": null, "cycle": 1, "debug": null, "errorFlag": null, "handWaves": 0, "humidity": 41.5, "id": 13210183611279476045, "light": 3, "pressure": null, "radonInstant": null, "radonLongTermAvg": null, "radonShortTermAvg": null, "record": 1357, "relayDevice": "iPhone", "relayDeviceOS": "iOS,15.3", "sampleRecorded": "2022-01-10T05:53:00", "sampleTransferred": "2022-01-11T06:36:12", "submitted": "2022-01-11T06:36:12", "temp": 20.219999313354492, "voc": null, "waveCcFwVersion": "1.5.3", "waveMspFwVersion": "1.6.0", "waveSub1FwVersion": "2.0.2" } ], "submitted": "2022-01-11T06:36:12" }

id is iterating +1 for every new record. The radon level is only transmitted once in a hour.

@LordEvron
Copy link

@Einstein2150 Thanks.. That is very useful. What setup did u use for intercepting the traffic?

@Einstein2150
Copy link

@Einstein2150 Thanks.. That is very useful. What setup did u use for intercepting the traffic?

mitmproxy running in docker

@Einstein2150
Copy link

Einstein2150 commented Jan 11, 2022

@LordEvron I wrote the first part for refreshing the token and getting the freshest cloud data. Seems like every old known token can call for a new one:

edit: removed and moved into https://github.com/Einstein2150/airthings-api-uploader

@LordEvron
Copy link

"Seems like every old known token can call for a new one" --> lol ..ok ...
it seems that you have the Wave plus, while i have the old wave. I do not have the MITM setup yet, but i will try to spend some time to set it up (i need it for extracting a working api key, since apparently they do not use that from the browser version). i will keep you posted.

@Einstein2150
Copy link

Einstein2150 commented Jan 11, 2022

@LordEvron here is the latest result. It is nearly working but something is preventing it from an update. I get no error but there gets nothing updated. Maybe the reason is that the id and the record isn't correctly calculated.

I have the Wave v2 - no hub and no plus 👍

here ist the latest working code:

edit: removed and moved into https://github.com/Einstein2150/airthings-api-uploader

@Einstein2150
Copy link

Damn. Bricked my cloud connection. Seems that playing with the post parameters got the data async. App synced but no more data gets saved in the cloud.

Unpairing and repairing fixed it but all cloud data is gone 🙃

Beware of that when playing with my code 🤓

@Einstein2150
Copy link

@LordEvron a hint for your first try: close the app for a night and start the mitm-proxy before opening the app. After a long period the app first calls for a fresh token. The token which requests in the https://api.airthin.gs/v1/refresh POST never expires and is the basic token which can always call for a new one 😁. This is the token for oldToken. In the next call to https://api.airthin.gs/v1/me?includeHubs=true you can extract the token for x-api-key. Have fun.

@Einstein2150
Copy link

I build a new repository for the API-stuff. Feel free to contribute:
https://github.com/Einstein2150/airthings-api-uploader

@LordEvron
Copy link

i have extracted the x-api-key .. but I noticed the app first get all the old datapoints from the cloud, then it push the new one, with subsequent unique ids, which are offsets from a specific date. So, I guess if you do not push data properly, you end up fu*king up your dataseries/cloud as you probably already did :D . I will need some more days/time to make sure i understand the logic behind the ids.

@LordEvron
Copy link

LordEvron commented Jan 18, 2022

So, for Wave version 1 the Post payload is different .. It post this data every hour and the IDs are subsequential ...

{
"samples": [
{"accel": 0,"accelEvent": false,"appState": "background","appVersion": "289(3.6.1)","battCharge": 145,"battVoltage": 249,"bleConnected": false,"cycle": 8,"errorFlag": false,"handWaves": 0,"humidity": 23.0,"id": "800000041","light": 0,"radonLongTermAvg": 20,"radonShortTermAvg": 23,"record": 41,"relayDevice": "samsung-SM-N5544","relayDeviceOS": "Android: 23","sampleId": "800000041","sampleRecorded": "2022-01-09T19:39:22","sampleTransferred": "2022-01-12T12:58:35","submitted": "2022-01-12T12:58:38","temp": 22.2,"waveCcFwVersion": "COR9001B.1703301","waveMspFwVersion": "2020-6-22"
},
{"accel": 0,"accelEvent": false,"appState": "background","appVersion": "289(3.6.1)","battCharge": 144,"battVoltage": 249,"bleConnected": false,"cycle": 8,"errorFlag": false,"handWaves": 0,"humidity": 23.0,"id": "800000042","light": 1,"radonLongTermAvg": 20,"radonShortTermAvg": 23,"record": 42,"relayDevice": "samsung-SM-N5544","relayDeviceOS": "Android: 23","sampleId": "800000042","sampleRecorded": "2022-01-09T20:39:22","sampleTransferred": "2022-01-12T12:58:35","submitted": "2022-01-12T12:58:38","temp": 22.2,"waveCcFwVersion": "COR9001B.1703301","waveMspFwVersion": "2020-6-22"
}
],
"submitted": "2022-01-12T13:58:38"
}

I hope this helps...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants