Skip to content

Navigation Menu

Sign in
Sign up

Repository files navigation

Suunto → Home Assistant (suunto_app)

A custom HACS integration that pulls your Suunto data into Home Assistant from the Suunto app (Sports Tracker) - signing in with just your email and password, no Docker and no partner keys.

Open your Home Assistant instance and open this repository inside the Home Assistant Community Store.

Suunto example dashboard

Example dashboard - live sensors plus backfilled long-term statistics (heart rate, training load, sleep).

Tip

Enjoying this integration? Every star is real motivation for me to keep developing it :)

Star this repo

Suunto watch ──▶ Suunto app / Sports Tracker ──▶ Home Assistant

⚠️ Unofficial integration - not affiliated with or endorsed by Suunto. It signs in with your own Suunto account and may stop working after a Suunto app update. Use your own account, at your own risk. Login pipeline ported from tajchert/suuntool.

Documentation

Full docs are in the project wiki : installation, every sensor, dashboard examples, derived metrics, long-term statistics and troubleshooting.

Custom Lovelace cards

Want a dashboard without wiring 94 sensors into generic entity/gauge cards by hand? Suunto Cards is a companion HACS repo with 53 purpose-built cards - last workout, HR zones, sleep & readiness, recovery, training load, a live 24/7 heart rate curve, an activity heatmap calendar, workout-to-workout comparisons, fun lifetime-distance equivalents, a computed training personality, a FIFA-style player card, 20 unlockable achievements, a game-style level/XP bar, an RPG training class, a next-milestone countdown, a lifetime story card, a 24h sleep clock and a sleep-regularity chart, and more. Each card auto-detects your Suunto device (zero YAML for the common case), themes with your Home Assistant theme automatically, and follows your HA language (English, Polish, German, Portuguese, French, Spanish, Italian, Dutch).

Suunto Cards preview

Installation & configuration

  1. Install via HACS (Custom repositories → this repo as an Integration) and restart HA.

  2. Settings → Devices & Services → Add Integration → "Suunto App (unofficial)" → enter the email and password of your Suunto app account. (Account 2FA may block login.)

  3. Options ("Configure" button): two refresh cadences -

    • Live data interval (default 15 min): current heart rate, daily steps/energy.
    • History interval (default 60 min): sleep, recovery, workouts, training load, baselines and other derived metrics - and the hourly long-term statistics (see below).

    Splitting the cadences keeps live values fresh without re-fetching ~90 days of history every few minutes.

Credential storage

The password is used only once (at setup), exchanged for a session token, and is not stored. Only the email and the revocable session token are written to HA's .storage. If the session ever expires, HA shows "reauthentication required" and asks for the password once (reauth) - the password is still not kept between times.

"New login" emails

Suunto sends a new-login notification on every /login2 call. The integration caches the session token and reuses it across restarts - it only logs in again on first setup or when the server invalidates the session. During normal operation (data fetching) it does not log in and does not generate emails.

Diagnostics

Settings → Devices & Services → the Suunto entry's ⋮ menu → Download diagnostics gives a redacted JSON dump of the integration's current state (useful when reporting a bug). Email, session token and GPS start coordinates are stripped; everything else - including the raw 24/7 sleep export used to build the sleep and nap sensors - is included as-is.

Entities (94 sensors + 3 binary sensors + a workouts calendar under one "Suunto" device)

Every entity name follows your Home Assistant language automatically - English, Polish, German, Portuguese, French, Spanish, Italian and Dutch are built in. Anything else falls back to English. (Display names only; entity_ids never change with your language.)

The device card itself shows your actual watch model (e.g. "Suunto 9 Peak Pro"), read from your most recent workout - not just "Suunto App (unofficial)".

  • Sleep: duration, stages (deep/light/REM), average/min heart rate, quality, SpO2, HRV, sleep start, wake-up time, and nap duration (tracked separately from night sleep so a nap never inflates it; state holds the most recent day that had a nap, with nap_count and date attributes since naps are irregular and the value can be several days old).
  • Recovery: recovery balance, stress state.
  • Daily activity: steps, active energy (kcal), current heart rate.
  • Last workout: type, start, days since (a rest-day counter - 0 means you trained today, handy as an automation trigger), start location (latitude/longitude - plots on a Map card, plus a downsampled route attribute with the full GPS track and per-point speed for a custom, pace-colored route card), distance, duration, recovery time, average/max heart rate, average speed (km/h) and pace (min/km), cadence (rpm - Suunto reports it as cycles/min for every sport; on foot-based activities the sensor also carries a cadence_spm attribute, the steps/min equivalent, so the state itself never changes and your history isn't rewritten), TSS (an alternative MET-based figure rides alongside it in the tss_met attribute, when Suunto computed one), time in 6 heart-rate zones (0-5), Peak Training Effect (Suunto's own 1-5 rating of the session), peak EPOC, your own feeling rating (1-5, when you set it on the watch), the workout type as Suunto classifies it (commute, strength, long aerobic base ...; the raw list is in the sensor's tags attribute, alongside is_manually_added - whether you typed the workout in rather than synced it from the watch), and recovered-at (when the recovery countdown ends). Each heart-rate zone sensor also carries its bpm range in the lower_limit_bpm / upper_limit_bpm attributes, so "38 min in zone 3" reads as an actual effort. Zone 0 is everything below zone 1, zone 1 is everything below zone 2 - most watches don't report a numeric split between zones 0 and 1 specifically, so zone 0 usually has no bpm range of its own even though its duration is always there; the top of zone 5 is your max heart rate.
  • Last workout - laps: state is how many laps the workout has; each lap in the laps attribute carries its own duration, distance and pace. 0 on a workout with no manual/auto laps, which is most of them.
  • Last workout - weather: on-site temperature (°C) as the sensor state, with humidity, wind speed (km/h), wind direction and a decoded condition (e.g. "Scattered clouds") in its attributes. Outdoor workouts only - unknown on an indoor session, since there's no weather to record.
  • Last workout - achievements: state is how many route achievements (e.g. "Fastest time on this route") the workout earned - 0 on most workouts, since Suunto only awards these on a route you've ridden/run before. The full raw list and this workout's route_ranking (if Suunto tracked one) are in the attributes.
  • Last workout - climbing: ascent and descent (m), time spent climbing and descending, and the altitude range (min/max). Indoor sessions have no barometer data, so the altitude sensors stay unknown there.
  • Lifetime stats: total distance (km), total time (h), total energy, number of workouts, active days, plus a per-sport breakdown (distance/time/count/energy for each activity type, in the sensor's attributes).
  • This year: the same five totals again (distance, time, energy, workouts, active days) scoped to the current calendar year instead of your whole history - a running "year in review". Resets on January 1st; the workouts sensor's attributes also carry the year's single most-common activity (e.g. "Cycling, 62% of this year's workouts").
  • This month: the same five totals once more, scoped to the current calendar month - resets on the 1st. Same shape as the yearly version above, including its own most-common-activity attribute.
  • Training records: state is your longest-ever workout streak (consecutive days); attributes carry four more all-time personal records - fastest pace, biggest single-workout climb, longest single workout, farthest single workout, and highest single-session TSS - each with the workout it happened in. Seeded once via a deep history scan (same technique the VO2max sensor uses) and only ever improved from there, so these are true lifetime bests, not bounded to the normal fetch window.
  • Training records - this month: the same five personal records, scoped to the current calendar month instead of your whole history - quietly resets on the 1st. No deep scan needed (a month always fits inside the normal fetch window), so it's always exactly in sync with this month's workouts.
  • Training records - this year: the same five personal records again, scoped to the current calendar year - a running "training year in review". Resets on January 1st; like the all-time sensor above (and unlike the monthly one) it needs its own deep history scan to pick up January's workouts once they've aged out of the normal fetch window.
  • Current streak: how many days in a row you've trained, right now - resets to 0 the moment a day is skipped. A different question from Training records' all-time longest streak above: that one only ever goes up, this one tracks whether you're on one today.
  • Best efforts: state is how many standard distances (1K, 5K, 10K, half marathon, marathon) have a recorded personal best so far; each one's time and the workout it happened in ride in attributes. Foot-based activities only, computed from the same detailed GPS/pace data already fetched for the route/lap sensors - the fastest continuous stretch covering at least that distance within a single workout. Tracked from when you install this version onward, not retroactively - a genuine best from before you updated won't be found unless you happen to beat it again.
  • Fitness: VO2max, estimated VO2max and fitness age, as measured by the watch. Suunto derives these from runs and walks only, so they hold their last reading between such workouts - each sensor's measured_at attribute shows when (and from which activity) it was taken. The same three numbers are also imported as a long-term statistics trend (see below), so you can chart them over time instead of only seeing today's held value.
  • Derived - training load: Fitness (CTL), Fatigue (ATL), Form (TSB) from TSS history, plus the acute:chronic workload ratio (ACWR; safe zone ~0.8-1.3), and a training suggestion (rest/easy/moderate/hard) for today, derived from those same two numbers - a spiking ACWR (>1.5) always suggests rest, regardless of how fresh your form looks.
  • Derived - recovery: HRV baseline + status (low/balanced/high), resting heart rate + baseline, and Readiness (0-100, a heuristic blending sleep, HRV, resting HR and recovery balance).
  • Derived - per workout: % of max HR, calories per km, ascent rate, stride length.
  • Weekly volume: workout distance, time and steps over the last 7 days (steps are read back from the hourly step statistics below, since they come from the 24/7 stream rather than the workout list).
  • Counts: workouts in the last 7 / 30 days.
  • Workouts calendar & recent list: a calendar entity with every past workout as a browsable event, plus a Recent workouts sensor whose attribute holds the last 60 (date, type, distance, duration, HR, TSS) - see below.
  • Binary sensors: Recovering (on while Suunto's recovery countdown from the last workout is still running), Workout today, and Unusual recovery (on when your resting heart rate is elevated and your HRV is suppressed at the same time, both vs. your own sleep-night baseline - a commonly used early signal of illness or overreaching, not a diagnosis; stays unknown until there is enough sleep history to have a real baseline). The first two flip on their own clock, so they change the moment the countdown ends or the day rolls over, without waiting for the next poll; Unusual recovery only changes when new sleep data arrives.

Automations: the new-workout event

When a workout first reaches the integration (i.e. after your watch has synced to the Suunto app), it fires a suunto_app_new_workout event on the Home Assistant bus, so you don't have to watch a sensor for changes:

automation:
 - alias: Notify me about a new workout
 triggers:
 - trigger: event
 event_type: suunto_app_new_workout
 actions:
 - action: notify.persistent_notification
 data:
 message: >
 {{ trigger.event.data.activity }}:
 {{ (trigger.event.data.distance_meters | float(0) / 1000) | round(1) }} km
 in {{ trigger.event.data.duration_minutes }} min,
 TSS {{ trigger.event.data.tss }}, PTE {{ trigger.event.data.pte }}

The event carries key, activity, activity_id, start_time, duration_minutes, distance_meters, avg_hr_bpm, max_hr_bpm, tss, pte, recovery_time_hours and tags. The first poll after a Home Assistant restart only takes stock of what already exists, and a workout that shows up more than a week after it happened is recorded silently - your history is never replayed as a burst of events.

Automation blueprints

Four ready-to-import blueprints under blueprints/automation/suunto_app/ wrap the patterns above so you don't have to write the YAML yourself - each just asks for an action (e.g. "Send a notification") and the entities/thresholds it needs:

Blueprint What it does Import
New Workout Notification Runs your action with a one-line workout summary whenever suunto_app_new_workout fires. Open your Home Assistant instance and show the blueprint import dialog with the new-workout-notification blueprint pre-filled.
Low Readiness Alert Runs your action once when the Readiness sensor drops below a threshold you set. Open your Home Assistant instance and show the blueprint import dialog with the low-readiness-alert blueprint pre-filled.
Unusual Recovery Alert Runs your action the moment the Unusual recovery sensor turns on. Open your Home Assistant instance and show the blueprint import dialog with the unusual-recovery-alert blueprint pre-filled.
Weekly Training Digest Runs your action with a weekly summary (workouts, distance, time, form) on the day(s)/time you pick. Open your Home Assistant instance and show the blueprint import dialog with the weekly-digest blueprint pre-filled.

Or import manually: Settings -> Automations & Scenes -> Blueprints -> Import Blueprint, and paste a blueprint's GitHub URL.

Derived metrics are computed locally in HA from history fetched via the API (sleep ~60 days, workouts ~90 days, paginated). CTL/ATL are seeded with the mean daily load to avoid an early-window underestimate. Readiness, training suggestion and unusual recovery are heuristics, not official Suunto metrics. All the math (CTL/ATL/TSB, ACWR, baseline, readiness, training suggestion, unusual recovery) is covered by deterministic tests in metrics.py.

Long-term statistics (intraday curves + backfill)

Suunto long-term statistics charts

Backfilled statistics: intraday heart rate (24/7 + workout peaks) and the Fitness / Fatigue / Form (CTL / ATL / TSB) trend.

Beyond the 94 live sensors, the integration imports hourly long-term statistics for the fast-changing and daily metrics. They are backfilled over a rolling window, so if your watch syncs to the app late (e.g. hours later), the missed hours are filled in retroactively - something a normal sensor can't do, since it only records the latest value at poll time.

These are external statistics (suunto_app:...), not entities - view them in a Statistics Graph card (or ApexCharts); they don't add to the sensor count.

  • Hourly: heart rate (mean/min/max - the 10-min 24/7 stream plus the dense ~25 s heart-rate samples from workouts, so workout peaks show up), steps, energy, recovery balance, stress.
  • Daily: sleep duration, HRV, resting heart rate, quality, SpO2; Readiness; the Fitness / Fatigue / Form (CTL/ATL/TSB) trend; peak Training Effect / peak EPOC (the day's hardest session, when there was more than one); and VO2max / estimated VO2max / fitness age - sparse, since Suunto only computes these from runs and walks, but every reading you get is charted as a point in the trend rather than only living in the live sensors' held state.

The backfill window is ~5 days - a sync delayed beyond that won't fill the part older than the window. The hourly heart-rate statistic is the way to see a gap-free daily HR curve (with workout peaks); the live current_hr sensor only steps to the newest synced value and can't be filled backwards.

Workouts calendar & recent activities

Suunto workouts calendar and recent activities list

Every past workout is exposed as an event on a calendar entity - browse your whole training history in a Calendar card, each event showing the activity, distance and key stats (duration, HR, TSS). A companion Recent workouts sensor keeps the last 60 sessions in its attributes for a compact list/table card. Both reuse the workout history already fetched - no extra requests.

Each entry in Recent workouts also carries cadence_spm and stride_length_m - the same running-dynamics figures behind the Cadence/Stride sensors below, present only for foot-based activities (running, walking, trekking), null otherwise - so a custom card can chart cadence or stride trend across your recent runs.

Workout start on a map

The Last workout location sensor carries the start latitude/longitude of your most recent workout as attributes, so it can be plotted directly on a Map card:

type: map
entities:
 - sensor.suunto_last_workout_location # your entity id (named after the account)

Indoor workouts with no GPS track show as unknown (no marker). The same start_lat / start_lon are also present on every entry of the Recent workouts sensor's attributes, if you'd like to plot more than just the latest one (e.g. with a template sensor or a custom card).

The same sensor also carries a route attribute: the last workout's full GPS track as a downsampled [[lat, lon, speed_kmh], ...] list (up to 300 points), each vertex carrying its own speed so a custom card can color the route by pace without a second data source. It's deliberately excluded from Home Assistant's recorder (only the live state matters for this), so it won't bloat your history database. route is absent on indoor workouts, same as latitude/longitude.

Lifetime totals per sport

The Lifetime by activity sensor's state is the number of activity types; the per-sport totals ride in its activities attribute (each with activity, workouts, distance_km, time_hours, energy_kcal). Render them with a Markdown card:

type: markdown
content: |
 | Sport | Workouts | Distance | Time |
 | --- | --: | --: | --: |
 {% for a in state_attr('sensor.suunto_lifetime_by_activity', 'activities') -%}
 | {{ a.activity }} | {{ a.workouts }} | {{ a.distance_km }} km | {{ a.time_hours }} h |
 {% endfor %}

Troubleshooting

  • "Login was rejected" - wrong email/password, or account 2FA.
  • "Reauthentication required" - the session expired; enter the password again.
  • Light/REM sleep sensors are unknown - your watch does not report them.
  • Daily energy dropped by ~4x after updating to 1.0.14 - that is the fix, not a regression. The value was previously read as calories when the API sends joules. It is active energy (above resting), so it is meant to be well below your total daily burn. Existing history is not rewritten, so expect a step in the graph; you can clear the old long-term statistics in Developer Tools > Statistics if the jump bothers you.
  • Altitude sensors are unknown after an indoor workout - intended. Without GPS or a barometer reading the watch reports no altitude, and showing 0 m would claim you trained at sea level.
  • Stride length is unknown - it is only computed for foot-based activities, so it stays empty after a ride.

Changelog

See CHANGELOG.md.

Disclaimer

An unofficial, experimental hobby project - run it at your own risk.

  • No ties to Suunto. Not affiliated with, endorsed by, or supported by Suunto Oy, Amer Sports, or Sports-Tracker. All trademarks stay with their owners.
  • Built on shifting ground. It talks to a private, undocumented endpoint that can change or stop working at any moment - a single app update may break it.
  • Possibly against Suunto's terms. Check them yourself. Hammering the service could get your account limited or closed; that's on you, not the author.
  • Your account only. Use it strictly for your own data - never to collect or aggregate anyone else's.
  • No warranty, no liability. Provided "as is", with no guarantees and no responsibility for anything that follows from using it.
  • Not legal advice. If any of this gives you second thoughts, just use the official Suunto app.

About

Unofficial Suunto watch integration for Home Assistant - sleep, recovery, workouts and training-load sensors

Topics

Resources

Stars

15 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /