project jira-projects / Miscellaneous avatar

jira-projects/MISC#35: Try and extract health data from Zepp



Issue Information

Issue Type: issue
Status: closed
Reported By: btasker
Assigned To: btasker

Created: 31-Jul-23 14:54



Description

I'm going to preface this with a caveat: I'm a little bit pissed off. I bought a Bip 3 watch because I wanted to link it to Gadgetbridge and keep health data local.

Unfortunately it's turned out (jira-projects/MISC#34) that that's not possible: the manufacturer appears to have changed the underlying firmware and Gadgetbridge doesn't have support for it. That'd be ok(ish) if the Zepp app provided a way to access the data without having to give it to Google Fit.

I've raised this ticket to track efforts in getting something up and running



Toggle State Changes

Activity


assigned to @btasker

assigned to @btasker

In theory there's probably a database in the phone's storage somewhere. However, it's not accessible via File Manager (the expected path is /data/data/com.huami.watch.hmwatchmanager/databases/) but that's probably because I'm not rooted.

Looking in the DNS logs, the app talks to api-mifit-de2.zepp.com

Screenshot_20230731_160135

I was going to MiTM it, but it looks like someone's already done the work

There's some code at https://github.com/rolandsz/Mi-Fit-and-Zepp-workout-exporter/tree/master, but it doesn't lend itself well to automation (because it has to pop a browser for login).

But, I've used it to help capture the underlying apptoken - need to test it later to see whether they have a finite lifetime, or if it's a set value. If the latter, then we should be able to use it to talk to their API.

Unfortunately, it looks like the Zepp app doesn't trust user-installed CA certs, so MiTMing to get paths isn't straight forward.

So, I think the options are:

  1. Try and figure out the paths (the project linked above shows that v1/sport/run/history.json is valid so we may be able to work out logical paths from that)
  2. Look at rooting the phone to see if we can get access to the local copy of the database
  3. Deal with the devil and set up Google Fit syncing so that we can pull the data via their API instead
  4. Return the watch: I really fucking hate closed eco-systems
  5. See whether some other app can talk to the watch and extract the data
  6. See whether we can automate going through their GDPR workflow - could then extract the data from the resulting CSVs

Doing 1. will be entirely contingent on the validity of the tokens. 2. is doable but ridiculous: I shouldn't need to root my phone to extract data generated by a device strapped to my wrist.

I'm quite keen not to do 3., I've got to quite a lot of trouble to push Google as far out of my life as I've currently managed, I don't really want to undo that.

4. is something of a last resort - I've got a few days to mess around before the window on that closes.

5. would be good, if a little overly complex. I guess a subset of 5. would be to see whether the app sends any intents when it syncs with the watch. It's just possible we might be able to do something with Tasker.

The frustrating thing is it looks like they used to have a documented web api.

I think the answer, for the time being, is to sleep on it. My inclination at the moment though, is to return it and buy something that does work with gadgetbridge (the problem, if we were to stick with Amznfit, is it looks like compatability may depend on firmware version).

It's such a shame the Bangle.js 2 is only IP67 rated, as that's what I was originally looking at. I settled for the Bip because I wanted to wear it whilst swimming.

The token still seems to work - it's worth noting that it requires that appplatform and appname headers also be submitted:

curl -v \
-H "AppPlatform: web" \
-H "appname: com.xiaomi.hm.health" \
-H "apptoken: $APP_TOKEN" \
https://api-mifit-de2.huami.com/v1/sport/run/history.json

Results in json

{"code":1,"message":"success","data":{"next":-1,"summary":[]}}

So, this might have legs...

Had another search around for resources that might be of use. Hacking the Mi Fit API looks particularly hopeful

That said, the following doesn't currently work

curl -v \
-H "AppPlatform: web" \
-H "appname: com.xiaomi.hm.health" \
-H "apptoken: $APP_TOKEN" \
-X GET \
--data-urlencode 'query_type=summary' \
--data-urlencode 'device_type=android_phone' \
--data-urlencode 'userid=7074932982' \
--data-urlencode 'from_date=2023-07-30' \
--data-urlencode 'to_date=2023-08-01' \
https://api-mifit.huami.com/v1/data/band_data.json

Tomorrow, I might look at reregistering for the app with email/pass rather than SSO. My guess though, is that I may need a different endpoint.

I've created a new Zepp account using an email rather than SSO - obviously there's currently no data to return, but, things do look potentially hopeful (I added a print to show the response body)

ben@optimus:~/Downloads$ python3 mifit_api.py --email <redacted> --password <redacted>
Logging in with email <redacted>
Obtained access token
Retrieveing mi band data
{'code': 1, 'message': 'success', 'data': []}

And actually, thinking about it, the script has a hardcoded from + to date in it. Adjusting those so they are no longer in 2019 leads to a response

ben@optimus:~/Downloads$ python3 mifit_api.py --email <redacted> --password <redacted>
Logging in with email <redacted>
Obtained access token
Retrieveing mi band data
2023-08-01
v = 6
Total sleep:  00:00 , deep sleep 00:00 , light sleep 00:00 , slept from 2023-07-31 00:00:00 until 2023-07-31 00:00:00
Total steps:  2234 , used 53 kcals , walked 1587 meters
09:59 - 10:17 725 steps light activity
10:19 - 10:35 860 steps slow walking
10:49 - 10:54 504 steps slow walking
goal = 8000
tz = 0
algv = 2.12.33
sn = 2171F311006533
byteLength = 8
sync = 1690884430

Looks like we're cooking on gas, just need to turn it into something that can write out to InfluxDB

OK, I've dropped a copy of the original script from https://github.com/micw/hacking-mifit-api into a repo.

So we need to

  • Make the date range it queries dynamic
  • Expand the stats it reports (it currently does sleep and steps, but not heartrate/stress etc)
  • Have stats written onwards into InfluxDB
  • Containerise

Expand the stats it reports (it currently does sleep and steps, but not heartrate/stress etc)

Let's start here.

The API response data looks like this

{
    "code":1,
    "message":"success",
    "data":[
        {
            "uid":"<redacted>",
            "data_type":0,
            "date_time":"2023-08-01",
            "source": 256,
            "summary":"eyJ2Ijo2LCJzbHAiOnsic3QiOjE2OTA3NTgwMDAsImVkIjoxNjkwNzU4MDAwLCJvYnQiOi0xMTc3OTk4MzM2LCJlYnQiOjEyMiwiZHAiOjAsImx0IjowLCJ3ayI6MCwidXNyU3QiOi0xNDQwLCJ1c3JFZCI6LTE0NDAsIndjIjowLCJzdXBOYXAiOnRydWUsInN1cFJlbSI6ZmFsc2UsImlzIjowLCJsYiI6MCwidG8iOjAsImR0IjowLCJyaHIiOjAsInNzIjowLCJwcyI6MCwicGUiOjQ4MH0sInN0cCI6eyJ0dGwiOjIyMzQsImRpcyI6MTU4NywiY2FsIjo1Mywid2siOjI1LCJybiI6MCwicnVuRGlzdCI6ODcsInJ1bkNhbCI6Nywic3RhZ2UiOlt7InN0YXJ0Ijo1OTksInN0b3AiOjYxNywibW9kZSI6NywiZGlzIjo1MjEsImNhbCI6MTcsInN0ZXAiOjcyNX0seyJzdGFydCI6NjE5LCJzdG9wIjo2MzUsIm1vZGUiOjEsImRpcyI6NTk4LCJjYWwiOjE4LCJzdGVwIjo4NjB9LHsic3RhcnQiOjY0OSwic3RvcCI6NjU0LCJtb2RlIjoxLCJkaXMiOjM2MCwiY2FsIjoxMiwic3RlcCI6NTA0fV19LCJnb2FsIjo4MDAwLCJ0eiI6IjAiLCJhbGd2IjoiMi4xMi4zMyIsInNuIjoiMjE3MUYzMTEwMDY1MzMiLCJieXRlTGVuZ3RoIjo4LCJzeW5jIjoxNjkwODg0NDMwfQ==",
            "uuid":
            "null"
        }
    ]
}

Each day is represented by a different dict within the list data. The attribute summary is a base64 encoded JSON blob

$ echo -n "eyJ2Ijo2LCJzbHAiOnsic3QiOjE2OTA3NTgwMDAsImVkIjoxNjkwNzU4MDAwLCJvYnQiOi0xMTc3OTk4MzM2LCJlYnQiOjEyMiwiZHAiOjAsImx0IjowLCJ3ayI6MCwidXNyU3QiOi0xNDQwLCJ1c3JFZCI6LTE0NDAsIndjIjowLCJzdXBOYXAiOnRydWUsInN1cFJlbSI6ZmFsc2UsImlzIjowLCJsYiI6MCwidG8iOjAsImR0IjowLCJyaHIiOjAsInNzIjowLCJwcyI6MCwicGUiOjQ4MH0sInN0cCI6eyJ0dGwiOjIyMzQsImRpcyI6MTU4NywiY2FsIjo1Mywid2siOjI1LCJybiI6MCwicnVuRGlzdCI6ODcsInJ1bkNhbCI6Nywic3RhZ2UiOlt7InN0YXJ0Ijo1OTksInN0b3AiOjYxNywibW9kZSI6NywiZGlzIjo1MjEsImNhbCI6MTcsInN0ZXAiOjcyNX0seyJzdGFydCI6NjE5LCJzdG9wIjo2MzUsIm1vZGUiOjEsImRpcyI6NTk4LCJjYWwiOjE4LCJzdGVwIjo4NjB9LHsic3RhcnQiOjY0OSwic3RvcCI6NjU0LCJtb2RlIjoxLCJkaXMiOjM2MCwiY2FsIjoxMiwic3RlcCI6NTA0fV19LCJnb2FsIjo4MDAwLCJ0eiI6IjAiLCJhbGd2IjoiMi4xMi4zMyIsInNuIjoiMjE3MUYzMTEwMDY1MzMiLCJieXRlTGVuZ3RoIjo4LCJzeW5jIjoxNjkwODg0NDMwfQ==" | base64 -d
{"v":6,"slp":{"st":1690758000,"ed":1690758000,"obt":-1177998336,"ebt":122,"dp":0,"lt":0,"wk":0,"usrSt":-1440,"usrEd":-1440,"wc":0,"supNap":true,"supRem":false,"is":0,"lb":0,"to":0,"dt":0,"rhr":0,"ss":0,"ps":0,"pe":480},"stp":{"ttl":2234,"dis":1587,"cal":53,"wk":25,"rn":0,"runDist":87,"runCal":7,"stage":[{"start":599,"stop":617,"mode":7,"dis":521,"cal":17,"step":725},{"start":619,"stop":635,"mode":1,"dis":598,"cal":18,"step":860},{"start":649,"stop":654,"mode":1,"dis":360,"cal":12,"step":504}]},"goal":8000,"tz":"0","algv":"2.12.33","sn":"2171F311006533","byteLength":8,"sync":1690884430}

We already know what some of these are:

  • slp - Sleep
  • stp - Steps

Annoyingly, it doesn't appear to expose the extra stats that we wanted (may need to go ahead and put some effort into MiTMing if we want those).

For now, then, we won't worry too much about capturing those extra details.

verified

mentioned in commit utilities/zepp_to_influxdb@dfe080653f432bc2776ff1f7fdcd026d31165fe0

Commit: utilities/zepp_to_influxdb@dfe080653f432bc2776ff1f7fdcd026d31165fe0 
Author: B Tasker                            
                            
Date: 2023-08-01T11:35:41.000+01:00 

Message

Import the InfluxDB client for jira-projects/MISC#35

We're not yet writing anything in, but the script should now take credentials from environment variables

+36 -5 (41 lines changed)

I'm cheating a little here and re-using some of the work I did for my gadgetbridge_to_influxdb script.

Need to have the script turn the values in the API response into something that can be consumed by the newly added function.

The format it expects is a list of dicts, with each dict being of the form

{
    "timestamp": r[0] * 1000000000, # Convert to nanos
    fields : {
        "intensity" : r[2],
        "steps" : r[3],
        "heart_rate" : r[5],
        "sleep" : r[6],
        "deep_sleep" : r[7],
        "rem_sleep" : r[8],
        },
    tags : {
        "device" : devices[f"dev-{r[1]}"],
        "activity_kind" : r[4],
        "sample_type" : "activity"
        }
}
verified

mentioned in commit utilities/zepp_to_influxdb@ea2c3f3493cf3ee79ea02feb20a9958a599c27be

Commit: utilities/zepp_to_influxdb@ea2c3f3493cf3ee79ea02feb20a9958a599c27be 
Author: B Tasker                            
                            
Date: 2023-08-01T12:06:51.000+01:00 

Message

Collect sleep data ready for submission into InfluxDB jira-projects/MISC#35

+79 -3 (82 lines changed)
verified

mentioned in commit utilities/zepp_to_influxdb@62c01dcb920918f5a4079372a388978327c62654

Commit: utilities/zepp_to_influxdb@62c01dcb920918f5a4079372a388978327c62654 
Author: B Tasker                            
                            
Date: 2023-08-01T12:16:44.000+01:00 

Message

Write sleep results onwards into InfluxDB (jira-projects/MISC#35)

This commit brings with it an unpleasant (but temporary) change.

Various variables have been moved to a global scope. This will be changed later when config is folded into a dict

+6 -9 (15 lines changed)
verified

mentioned in commit utilities/zepp_to_influxdb@7b4d39d3b365fd48cc795b972987f2f123c2704f

Commit: utilities/zepp_to_influxdb@7b4d39d3b365fd48cc795b972987f2f123c2704f 
Author: B Tasker                            
                            
Date: 2023-08-01T12:30:31.000+01:00 

Message

Extract step data jira-projects/MISC#35

I've just spotted an issue though, the stage timestamps are not what I had expected. I thought they were essentially epoch/60 but they're actually the minute of the day (i.e. 600 = 10am).

So we need to adjust the way that these timestamps are converted

+50 -1 (51 lines changed)
verified

mentioned in commit utilities/zepp_to_influxdb@3f4b3be40132c4ce0a5b9ce7a6865f3529df7753

Commit: utilities/zepp_to_influxdb@3f4b3be40132c4ce0a5b9ce7a6865f3529df7753 
Author: B Tasker                            
                            
Date: 2023-08-01T12:37:51.000+01:00 

Message

Convert minutes of the day into an epoch timestamp (jira-projects/MISC#35)

This combines the record's day with minutes of the day to generate a timestamp

+18 -6 (24 lines changed)
verified

mentioned in commit utilities/zepp_to_influxdb@be2aac561b3f2a5ad5b0920b1f4b6e4e56b853b6

Commit: utilities/zepp_to_influxdb@be2aac561b3f2a5ad5b0920b1f4b6e4e56b853b6 
Author: B Tasker                            
                            
Date: 2023-08-01T12:41:47.000+01:00 

Message

Capture step goal and append device serial number as a tag (jira-projects/MISC#35)

+19 -5 (24 lines changed)
verified

mentioned in commit utilities/zepp_to_influxdb@a65b90704fd34b2d83a565d65547571981f64cfd

Commit: utilities/zepp_to_influxdb@a65b90704fd34b2d83a565d65547571981f64cfd 
Author: B Tasker                            
                            
Date: 2023-08-01T13:30:40.000+01:00 

Message

Dynamically construct timerange to query (jira-projects/MISC#35)

By default we'll ask the API for the last 2 days, however this can be overridden using env var QUERY_DURATION

+23 -14 (37 lines changed)
verified

mentioned in commit utilities/zepp_to_influxdb@65dd8dc86fd460a84394259f30428e78f0b29692

Commit: utilities/zepp_to_influxdb@65dd8dc86fd460a84394259f30428e78f0b29692 
Author: B Tasker                            
                            
Date: 2023-08-01T13:44:08.000+01:00 

Message

Containerise application (jira-projects/MISC#35)

+12 -0 (12 lines changed)

OK, as we've got a viable container, I've started setting up to have it as a Kubernetes cronjob.

Creating secrets

kubectl create secret generic zepp --from-literal='email=<redacted>' --from-literal='pass=<redacted>'

kubectl create secret generic influxdbv1 \
--from-literal=influxdb_token='<redacted>' \
--from-literal=influxdb_org='<redacted>' \
--from-literal=influxdb_url='http://<redacted>:8086'

Defined the job

apiVersion: batch/v1
kind: Job
metadata:
  name: zepp-to-influxdb
spec:
  template:
    spec:
          containers:
          - name: zepp-to-influxdb
            image: bentasker12/zepp_to_influxdb:latest
            imagePullPolicy: IfNotPresent
            env:

            - name: INFLUXDB_BUCKET
              value: "telegraf"
            - name: INFLUXDB_MEASUREMENT
              value: "zepp"
            - name: QUERY_DURATION
              value: "2"

            - name: INFLUXDB_TOKEN
              valueFrom: 
                 secretKeyRef:
                    name: influxdbv1
                    key: influxdb_token
            - name: INFLUXDB_ORG
              valueFrom: 
                 secretKeyRef:
                    name: influxdbv1
                    key: influxdb_org
            - name: INFLUXDB_URL
              valueFrom: 
                 secretKeyRef:
                    name: influxdbv1
                    key: influxdb_url
            - name: ZEPP_EMAIL
              valueFrom: 
                 secretKeyRef:
                    name: zepp
                    key: email
            - name: ZEPP_PASS
              valueFrom: 
                 secretKeyRef:
                    name: zepp
                    key: pass

          restartPolicy: OnFailure

Applied it

kubectl apply -f zepp_job.yml

Listing jobs

ben@bumblebee:~/charts$ kubectl get job --all-namespaces
NAMESPACE       NAME                             COMPLETIONS   DURATION   AGE
default         zepp-to-influxdb                 1/1           11s        3m1s
ingress-nginx   ingress-nginx-admission-create   1/1           9s         6d5h
ingress-nginx   ingress-nginx-admission-patch    1/1           10s        6d5h

Get the name of the pod that ran the job

kubectl describe job zepp-to-influxdb | grep "Created pod" | awk '{print $NF}'

Pass the podname to kubectl logs

kubectl logs zepp-to-influxdb-89qbv
Logging in with email <redacted>
Obtained access token
Retrieving mi band data
2023-08-01
Skipped v = 6
Skipped tz = 0
Skipped algv = 2.12.33
Skipped byteLength = 8

As that worked, then, defining a cronjob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: zepp-to-influxdb
spec:
  schedule: "50 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: zepp-to-influxdb
            image: bentasker12/zepp_to_influxdb:latest
            imagePullPolicy: IfNotPresent
            env:

            - name: INFLUXDB_BUCKET
              value: "telegraf"
            - name: INFLUXDB_MEASUREMENT
              value: "zepp"
            - name: QUERY_DURATION
              value: "2"

            - name: INFLUXDB_TOKEN
              valueFrom: 
                 secretKeyRef:
                    name: influxdbv1
                    key: influxdb_token
            - name: INFLUXDB_ORG
              valueFrom: 
                 secretKeyRef:
                    name: influxdbv1
                    key: influxdb_org
            - name: INFLUXDB_URL
              valueFrom: 
                 secretKeyRef:
                    name: influxdbv1
                   key: influxdb_url
            - name: ZEPP_EMAIL
              valueFrom: 
                 secretKeyRef:
                    name: zepp
                    key: email
            - name: ZEPP_PASS
              valueFrom: 
                 secretKeyRef:
                    name: zepp
                    key: pass

          restartPolicy: OnFailure

Enabling

kubectl apply -f zepp_cron.yml

It should now run once an hour. It looks like the watch syncs to the app (and the app to servers) less frequently than that, so we shouldn't really need it to run more regularly.

verified

mentioned in commit utilities/zepp_to_influxdb@e3462ece25de395a234309d6b9f9e655efce32c2

Commit: utilities/zepp_to_influxdb@e3462ece25de395a234309d6b9f9e655efce32c2 
Author: B Tasker                            
                            
Date: 2023-08-01T16:20:06.000+01:00 

Message

Add README (jira-projects/MISC#35)

+138 -0 (138 lines changed)

Querying stats out then:

Total steps per day for the last week

SELECT max("total_steps") AS "total_steps" FROM "telegraf"."autogen"."zepp" WHERE time > now() - 7d AND "activity_type"='steps' GROUP BY time(1d) FILL(null)

Calories burnt per day for the last week

SELECT max("calories") AS "calories" FROM "telegraf"."autogen"."zepp" WHERE time > now() - 7d AND "activity_type"='steps' GROUP BY time(1d) FILL(null)

Number of recorded activities per day

SELECT max("recorded_activities") AS "activites" FROM "telegraf"."autogen"."zepp" WHERE time > now() - 7d GROUP BY time(1d) FILL(null)

Time spent in activities per day

SELECT sum("activity_duration_m")*60 AS "activity_duration_s" FROM "telegraf"."autogen"."zepp" WHERE time > now() - 7d AND "activity_type"!='steps' GROUP BY time(1d) FILL(null)

Sleep stats

SELECT max("deep_sleep_min") AS "deep_sleep_min", max("rem_sleep_min") AS "rem_sleep_min", max("total_sleep_min") AS "total_sleep_min" FROM "telegraf"."autogen"."zepp" WHERE time > now() - 7d AND "activity_type"='sleep' GROUP BY time(1d) FILL(null)

I've created a new public repo at https://github.com/bentasker/zepp_to_influxdb and pushed into that.

I think we're up and running - anything extra should be dropped into a new ticket within the relevant project.

I've set up some downsampling of the data - it's a little finger in the air, but should allow me to extract interesting stats

downsample_zepp_stepcount:
    # Name for the task
    name: "Downsample Zepp Step count"
    influx: home1x

    # Query the last n mins
    period: 180

    # Window into n minute blocks
    window: 60

    # taken from in_bucket
    bucket: telegraf
    measurement:
        - zepp

    fields:
        - total_steps
        - goal

    filters:
        - '(r.activity_type == "steps" or r._field == "goal")'

    aggregates: 
        max:
            as: 
                total_steps: "steps"
                goal: "goal"

    output_influx: 
        - influx: home2xreal
    output_bucket: health    

downsample_zepp_distance_and_calories:
    # Name for the task
    name: "Downsample Zepp distance and calories"
    influx: home1x

    # Query the last n mins
    period: 180

    # Window into n minute blocks
    window: 60

    # taken from in_bucket
    bucket: telegraf
    measurement:
        - zepp

    fields:
        - calories
        - distance_m

    filters:
        - 'r.activity_type == "steps"'

    aggregates: 
        max:

    output_influx: 
        - influx: home2xreal
    output_bucket: health    

downsample_zepp_mean_distance_and_calories:
    # Name for the task
    name: "Downsample Zepp Activity distance and calories"
    influx: home1x

    # Query the last n mins
    period: 180

    # Window into n minute blocks
    window: 60

    # taken from in_bucket
    bucket: telegraf
    measurement:
        - zepp

    fields:
        - calories
        - distance_m
        - activity_duration_m

    filters:
        - 'r.activity_type != "steps"'

    aggregates: 
        mean:
            field_suffix: "_mean"
        count:
            field_suffix: "_count"            
        max:
            field_suffix: "_max"            

    output_influx: 
        - influx: home2xreal
    output_bucket: health    

downsample_zepp_activitycount:
    # Name for the task
    name: "Downsample Zepp activity count"
    influx: home1x

    # Query the last n mins
    period: 180

    # Window into n minute blocks
    window: 60

    # taken from in_bucket
    bucket: telegraf
    measurement:
        - zepp

    fields:
        - recorded_activities
        - recorded_light_activity_events
        - recorded_slow_walking_events
        - recorded_fast_walking_events
        - recorded_running_events        

    aggregates: 
        max:

    output_influx: 
        - influx: home2xreal
    output_bucket: health


downsample_zepp_sleepdata:
    # Name for the task
    name: "Downsample Zepp sleep data"
    influx: home1x

    # Query the last n mins
    period: 180

    # Window into n minute blocks
    window: 60

    # taken from in_bucket
    bucket: telegraf
    measurement:
        - zepp

    filters:
        - 'r.activity_type == "sleep"'

    fields:
        - deep_sleep_min
        - rem_sleep_min
        - total_sleep_min        


    aggregates: 
        max:

    output_influx: 
        - influx: home2xreal
    output_bucket: health


downsample_zepp_sleepdata_mean:
    # Name for the task
    name: "Downsample Zepp per-stage sleep data"
    influx: home1x

    # Query the last n mins
    period: 180

    # Window into n minute blocks
    window: 60

    # taken from in_bucket
    bucket: telegraf
    measurement:
        - zepp

    filters:
        - 'r.activity_type == "sleep_stage"'

    fields:
        - deep_sleep_min
        - rem_sleep_min
        - total_sleep_min        


    aggregates: 
        mean:
            field_suffix: "_mean"
        count:
            field_suffix: "_count"
        max:
            field_suffix: "_max"

    output_influx: 
        - influx: home2xreal
    output_bucket: health    


downsample_zepp_sleepdata_count:
    # Name for the task
    name: "Downsample Zepp sleep counts"
    influx: home1x

    # Query the last n mins
    period: 180

    # Window into n minute blocks
    window: 60

    # taken from in_bucket
    bucket: telegraf
    measurement:
        - zepp

    fields:
        - recorded_sleep_events
        - recorded_light_sleep_events
        - recorded_deep_sleep_events

    aggregates: 
        max:

    output_influx: 
        - influx: home2xreal
    output_bucket: health   

Broadly, what we're collecting is

Incremental daily totals:

  • Step count
  • Step goal
  • Daily calorie burn
  • Distance covered (metres)
  • Number of activities recorded
  • Number of activities recorded by type
  • Minutes spent asleep
  • Minutes spent in REM sleep
  • Minutes spent in deep sleep
  • Number of recorded sleep events
  • Number of recorded sleep events by stage

Per-activity type:

  • Mean calorie burn
  • Max calorie burn
  • Activity count

Per Sleep Stage:

  • Mean minutes spent
  • Max minutes spent
  • Instances of stage in the window

mentioned in commit sysconfigs/downsample_configs@3b592467c9d57f7e7993d2c308b51f9a6673df3a

Commit: sysconfigs/downsample_configs@3b592467c9d57f7e7993d2c308b51f9a6673df3a 
Author: ben                            
                            
Date: 2023-08-01T17:50:02.000+01:00 

Message

Add downsampling task for data added in jira-projects/MISC#35

+229 -0 (229 lines changed)

I think we're up and running - anything extra should be dropped into a new ticket within the relevant project.

I've made a few improvements since this comment. Issue tracking is in utilities/zepp_to_influxdb.