Skip to content

feat(cardio): auto-log treadmill workouts from MQTT#43

Merged
rwlove merged 1 commit into
mainfrom
feat/treadmill-cardio-mqtt
Jun 24, 2026
Merged

feat(cardio): auto-log treadmill workouts from MQTT#43
rwlove merged 1 commit into
mainfrom
feat/treadmill-cardio-mqtt

Conversation

@rwlove

@rwlove rwlove commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

PUMP now auto-logs gym treadmill workouts as cardio by subscribing to the treadmill's Z-Wave metering smart plug. The plug's wattage is published to the EMQX broker by zwave-js-ui (independent of Home Assistant), so PUMP consumes it directly — no HA in the path.

How it works

  • internal/treadmill/session.go — a pure threshold + off-debounce + min-session state machine (no I/O), so it's directly unit-tested.
  • internal/treadmill/consumer.go — paho MQTT client (non-blocking connect, auto-reconnect, re-subscribe on reconnect). Parses zwave-js-ui's {"time","value"} payload, feeds wattage to the detector, and a 15 s tick loop closes a session once the plug goes idle. Each session is written via the existing HealthStore.InsertHealthRecords, deduped on (metric_type, start_time, end_time).
  • Surfaces on Stats → Cardio as a CardioSession (no schema change — health_record v8 already models exercise records).

Duration-only by design — a smart plug can't measure belt speed/distance.

Config

Gated by TREADMILL_MQTT_ENABLED; all TREADMILL_MQTT_* vars documented in main.go and the README env table. Production threshold is 100 W to match HA's binary_sensor.treadmill helper.

Tests

go test ./... passes; new table-driven tests cover normal session, brief-dip-doesn't-split, sub-2-min discarded, late-sample close, and never-above.

🤖 Generated with Claude Code

Subscribe to the gym treadmill's Z-Wave metering smart plug — its
wattage is published to EMQX by zwave-js-ui, independent of Home
Assistant — and record each detected workout as an "exercise"
HealthRecord that surfaces on the Stats cardio view.

New internal/treadmill package:
  - session.go: a pure threshold + off-debounce + min-session state
    machine (no I/O), unit-tested in session_test.go.
  - consumer.go: paho MQTT client (non-blocking connect, auto-reconnect)
    that feeds wattage samples to the detector and a tick loop that
    closes a session once the plug goes idle, then writes it via the
    existing HealthStore.InsertHealthRecords (deduped on
    metric_type/start/end).

Duration-only — a smart plug can't measure belt speed/distance. No DB
schema change: health_record (v8) already models exercise records.

Gated by TREADMILL_MQTT_ENABLED; all TREADMILL_MQTT_* env documented in
main.go and the README env table.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
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

Successfully merging this pull request may close these issues.

1 participant