feat(trackpoints): add per-trip GPS trackpoints endpoint#207
Conversation
Adds SmartAccount.get_trip_trackpoints(vin, start_time_ms, end_time_ms),
backed by /vehicle-history-service/journal-service/vehicle/status/history/{VIN}
on the same apiv2.ecloudeu.com host as journalLogV4. Returns a
TripTrackpoints dataclass with chronologically-ordered Trackpoints
(cloud sends desc; wrapper reverses at the boundary). Coordinates are
converted from cloud milliarcseconds to WGS84 decimal degrees, matching
the scaling already used by pysmarthashtag.vehicle.position.Position.
Unlike journalLogV4, this endpoint does NOT require the
grant_journal_authorization handshake — calling it defensively per
fetch would burn an extra cloud round-trip and risks transient 7065
errors on back-to-back grants.
Three benign-empty cloud responses (1000+empty list, 1000+null data,
8153 "data unavailable") all normalise to TripTrackpoints(points=[],
total_size=0). Other HTTPStatusError exceptions propagate so callers
can decide whether to retry or surface the error.
|
Warning Review limit reached
More reviews will be available in 59 minutes and 36 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more credits in the billing tab to continue. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Adds
account.get_trip_trackpoints(vin, start_time_ms, end_time_ms, page_size=500)against/vehicle-history-service/journal-service/vehicle/status/history/{VIN}onapiv2.ecloudeu.com. A trip is identified by its(start_time_ms, end_time_ms)window (the endpoint has no trip-id). ReturnsTripTrackpoints(points=[Trackpoint(lat, lon), ...], total_size)in chronological order, with lat/lon as WGS84 decimal degrees.Complements
get_trip_journalfrom #194: that endpoint gives per-trip metrics plus the start/end positions embedded injournalLogV4; this one gives the GPS trail between them.What's added
pysmarthashtag/vehicle/trackpoints.py—TrackpointandTripTrackpointsfrozen dataclasses, plusparse_trackpoints_response.pysmarthashtag/account.py—get_trip_trackpoints, module-levelTRIP_TRACKPOINTS_PATH_PREFIX,TRIP_TRACKPOINTS_PAGE_SIZE, and a private_is_benign_emptyhelper.pysmarthashtag/tests/test_trackpoints.py(11 tests) andpysmarthashtag/tests/replys/trackpoints_response.json(4-point newest-first fixture).pysmarthashtag/tests/common.py—SmartMockRouterregisters the trackpoints path prefix for both test VINs on each base URL.Design choices worth flagging
No
grant_journal_authorizationceremony. UnlikejournalLogV4from #194, this endpoint is not gated behind the per-sessionauthorization/inserthandshake. Calling grant defensively per fetch would add a cloud round-trip per trip and risks transient7065on back-to-back grants. We observed across sessions that the minimum flow is auth at startup (already done byget_vehicles) plus the single signedGET.test_get_trip_trackpoints_does_not_call_grantregistersauthorization/inserton both base URLs and asserts zero hits.pageSize=500,pageIndex=0, no page loop. Page size is fixed atTRIP_TRACKPOINTS_PAGE_SIZE = 500, matching the cap the cloud advertises in its ownpaginationblock. In observed responsestotleSizenever exceeded this. Rather than ship dead pagination code, the wrapper emits aWARNING("page-size cap exceeded ... Wrapper does not loop pageIndex; returned points are the head only. Add pagination if this fires for real trips.") and still returns the truncated head. Locked bytest_get_trip_trackpoints_warns_on_oversize_total. Caller can overridepage_size. Note: this endpoint is 0-indexed;journalLogV4is 1-indexed — empirical, not a typo.descreversed to chronological at the wrapper boundary. The cloud reportspagination.direction = "desc"(newest-first).parse_trackpoints_responsereverses once viareversed(items)so every consumer seespoints[0]= trip start,points[-1]= trip end. Teststest_parse_reverses_to_chronological_orderandtest_get_trip_trackpoints_returns_chronological_pointslock this.Coordinate scaling — wire format, not Position-equivalence. The cloud transmits lat/lon as integer milliarcseconds;
Trackpointexposes WGS84 decimal degrees (mas / 3,600,000 via_MAS_PER_DEGREE). The existingpysmarthashtag.vehicle.position.Positionstores raw mas integers without conversion —Trackpoint's decimal-degree convention is the more consumer-friendly representation for downstream consumers (e.g. HA trip-log integrations needing plottable WGS84). Happy to add aPosition.lat_degreesproperty in a follow-up if preferred; docstring will be reworded to make the divergence explicit rather than claim equivalence.Defensive parsing. Every nesting level (
response_data,data,data.list,data.pagination, eachitem,basicVehicleStatus,position) isisinstance-guarded. Degraded entries yieldTrackpoint(lat=None, lon=None)placeholders rather than dropping out — sequence integrity over per-point completeness.total_sizefalls back tolen(items)whentotleSizeis missing or non-numeric.Three benign-empty paths collapse to
TripTrackpoints(points=[], total_size=0):code "1000"+ empty list,code "1000"+data: null, andcode "8153""data unavailable". The 8153 case arrives ashttpx.HTTPStatusErrorfrom the lower layer;_is_benign_emptyJSON-decodes the response body and matches oncode == "8153". All otherHTTPStatusErrors re-raise — locked bytest_get_trip_trackpoints_normalises_8153andtest_get_trip_trackpoints_propagates_other_http_errors(usescode: "5000").No refresh-path coupling.
get_trip_trackpointsis caller opt-in; it is not invoked from_refresh_vehicle_data. Adding this endpoint cannot make periodic refresh more fragile.Tests
Parser-only:
test_parse_handles_none_and_non_dict,test_parse_handles_empty_data_block,test_parse_handles_empty_list_with_zero_total,test_parse_reverses_to_chronological_order,test_parse_milliarcsecond_to_degree_scaling,test_parse_handles_degraded_per_point_payload,test_parse_non_list_payload_coerces_to_empty.End-to-end (
respx):test_get_trip_trackpoints_returns_chronological_points,test_get_trip_trackpoints_does_not_call_grant,test_get_trip_trackpoints_normalises_8153,test_get_trip_trackpoints_propagates_other_http_errors,test_get_trip_trackpoints_warns_on_oversize_total.Signing reuses the existing
utils.generate_default_headerHMAC path — no changes to auth or request signing.