Detect a live LiveTrack session from its last point, not session["end"]
session["end"].nil? was the active signal since this was first built, on the assumption Garmin leaves it unset until the activity actually stops. Checked directly against a real still-recording session today (2026-09-20): end was already non-nil, set to exactly start + 24h — the share link's own expiry, not an end time — so every currently-active session was showing as "Ended". A genuinely ended session's end does hold its real end time (confirmed against the 2026-09-06 session this was originally, and only, verified against), which is what made the bug easy to miss. Recency of the last track point is a directly-observable signal instead of reverse-engineering what a given `end` value means: Garmin posts one roughly every postTrackPointFrequency seconds (~15, observed) while genuinely still recording.
This commit is contained in:
1 parent
b7618edc25
commit
5d86cb1d04
2 files changed
+38
-8
No files matched your search
@@ -1,5 +1,6 @@
|
||||
require "net/http"
|
||||
require "json"
|
||||
require "time"
|
||||
|
||||
module Maptool
|
||||
# Fetches a Garmin LiveTrack session's track and metadata.
|
||||
@@ -44,10 +45,22 @@ module Maptool
|
||||
# totals are cumulative for the whole session), name/type from the
|
||||
# session details — nil-safe throughout since either can be missing.
|
||||
#
|
||||
# "active": Garmin doesn't set a session's `end` until the activity is
|
||||
# actually stopped, so its absence means the session is still recording
|
||||
# — inferred from the one (already-ended) session this was built
|
||||
# against, not verified against a session actually caught mid-activity.
|
||||
# "active": originally `session["end"].nil?`, on the assumption Garmin
|
||||
# doesn't set `end` until the activity actually stops — disproven
|
||||
# against a real still-recording session (2026-09-20): `end` was
|
||||
# already non-nil, set to exactly `start + 24h`, the share link's own
|
||||
# expiry, not an actual end time (a genuinely ended session's `end`
|
||||
# instead reflects its real end — ~2h after start for the 2026-09-06
|
||||
# session this was originally, and only, verified against). Recency of
|
||||
# the last track point is a directly-observable signal instead of
|
||||
# trying to reverse-engineer what a given `end` value means: Garmin
|
||||
# posts one roughly every `postTrackPointFrequency` seconds (~15,
|
||||
# observed) while genuinely still recording, so a point older than
|
||||
# ACTIVE_WITHIN means the activity has actually stopped (or, at worst,
|
||||
# lost signal for a while — same "looks ended until it isn't" behavior
|
||||
# Garmin's own LiveTrack page shows in that case).
|
||||
ACTIVE_WITHIN = 30 * 60 # seconds
|
||||
|
||||
def self.summarize(result)
|
||||
last = result.points.last || {}
|
||||
session = result.session || {}
|
||||
@@ -56,12 +69,20 @@ module Maptool
|
||||
"activity_type" => last["activityType"],
|
||||
"distance_meters" => last["totalDistanceMeters"],
|
||||
"duration_secs" => last["totalDurationSecs"],
|
||||
"active" => session["end"].nil?,
|
||||
"active" => active?(last["dateTime"]),
|
||||
"start" => session["start"],
|
||||
"end" => session["end"],
|
||||
}
|
||||
end
|
||||
|
||||
def self.active?(last_point_time)
|
||||
return false unless last_point_time
|
||||
|
||||
Time.parse(last_point_time) > Time.now.utc - ACTIVE_WITHIN
|
||||
rescue ArgumentError, TypeError
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# The RSC payload serializes an absent field as the literal string
|
||||
|
||||
Reference in new issue
Block a user