Public/Get-GarminLastActivity.ps1
|
function Get-GarminLastActivity { [CmdletBinding()] param( [Parameter(Mandatory = $false)] [Microsoft.PowerShell.Commands.WebRequestSession]$Session = $Global:GarminLoginSession, [switch]$Raw ) if ($null -eq $Session) { throw 'No Garmin session found. Run New-GarminLoginSession first.' } $headers = @{ 'User-Agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' 'accept' = 'application/json, text/plain, */*' 'accept-language' = 'en-US,en;q=0.9,de;q=0.8' 'origin' = 'https://connect.garmin.com' 'referer' = 'https://connect.garmin.com/modern/activities' } $candidateUris = @( 'https://connect.garmin.com/modern/proxy/activitylist-service/activities/search/activities?start=0&limit=1', 'https://connect.garmin.com/modern/proxy/activitylist-service/activities/search/activities?start=0&limit=1&sortColumn=startTimeLocal&sortOrder=desc' ) $lastError = $null $response = $null foreach ($uri in $candidateUris) { try { $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $headers -WebSession $Session -ErrorAction Stop if ($null -ne $response) { break } } catch { $lastError = $_ } } if ($null -eq $response) { if ($null -ne $lastError) { throw $lastError } throw 'No response received from Garmin activity endpoints.' } $responseAsString = [string]$response $isSignInHtmlResponse = ( -not [string]::IsNullOrWhiteSpace($responseAsString) -and $responseAsString -match '(?is)<title>\s*Garmin Connect\s*\|\s*Sign In\s*</title>|signin\.astro|please enable JavaScript in your web browser' ) if ($isSignInHtmlResponse) { throw 'Garmin session is not authenticated for Connect APIs. The endpoint returned the Sign-In page. Please run New-GarminLoginSession again in the same terminal session.' } $getFirstActivity = { param( [Parameter(Mandatory = $true)] [object]$InputObject ) if ($null -eq $InputObject) { return $null } if ($InputObject -is [array]) { foreach ($item in $InputObject) { $found = & $getFirstActivity -InputObject $item if ($null -ne $found) { return $found } } return $null } if ($InputObject -is [System.Collections.IEnumerable] -and -not ($InputObject -is [string])) { foreach ($item in $InputObject) { $found = & $getFirstActivity -InputObject $item if ($null -ne $found) { return $found } } return $null } $propertyBag = $InputObject.PSObject.Properties if ($null -eq $propertyBag) { return $null } $looksLikeActivity = ( $propertyBag.Name -contains 'activityId' -or $propertyBag.Name -contains 'activityName' -or $propertyBag.Name -contains 'startTimeLocal' ) if ($looksLikeActivity) { return $InputObject } $preferredContainers = @('activities', 'activityList', 'results', 'items', 'data', 'content') foreach ($containerName in $preferredContainers) { $containerProp = $propertyBag | Where-Object { $_.Name -eq $containerName } | Select-Object -First 1 if ($null -ne $containerProp) { $found = & $getFirstActivity -InputObject $containerProp.Value if ($null -ne $found) { return $found } } } foreach ($prop in $propertyBag) { $found = & $getFirstActivity -InputObject $prop.Value if ($null -ne $found) { return $found } } return $null } $activity = $null $activity = & $getFirstActivity -InputObject $response if ($Raw) { return [pscustomobject]@{ ParsedActivity = $activity Response = $response IsSignInHtml = $isSignInHtmlResponse } } if ($null -eq $activity) { throw 'Connected, but no activity could be parsed from response.' } $activityType = $null if ($null -ne $activity.activityType) { if ($activity.activityType.PSObject.Properties.Name -contains 'typeKey') { $activityType = $activity.activityType.typeKey } elseif ($activity.activityType -is [string]) { $activityType = $activity.activityType } } $durationSeconds = $null if ($null -ne $activity.duration) { $durationSeconds = $activity.duration } elseif ($null -ne $activity.elapsedDuration) { $durationSeconds = $activity.elapsedDuration } $distanceKm = $null if ($null -ne $activity.distance) { $distanceKm = [math]::Round(([double]$activity.distance / 1000.0), 2) } return [pscustomobject]@{ Connected = $true ActivityId = $activity.activityId ActivityName = $activity.activityName ActivityType = $activityType StartTimeLocal = $activity.startTimeLocal DurationSeconds = $durationSeconds DistanceKm = $distanceKm ElevationGainMeters = $activity.elevationGain } } |