Skip to content

Make Hyundai Europe location parsing robust#33

Open
dz0ny wants to merge 1 commit into
schmidtwmark:mainfrom
dz0ny:fix/hyundai-eu-location-parsing
Open

Make Hyundai Europe location parsing robust#33
dz0ny wants to merge 1 commit into
schmidtwmark:mainfrom
dz0ny:fix/hyundai-eu-location-parsing

Conversation

@dz0ny

@dz0ny dz0ny commented May 26, 2026

Copy link
Copy Markdown

Problem

On Hyundai Europe vehicles, status syncs return everything (battery, lock, climate, odometer, tyres) except the car location, which shows as missing.

Root cause

parseLocation chose between the two location sources purely by timestamp:

  • …/location/park (a cached park snapshot)
  • the location embedded in the status response (vehicleLocation)

It returned the /location/park branch whenever its date was newer — even when that endpoint returned no coordinates (the car has had no recent park event). Combined with a status body that carries no embedded vehicleLocation, the location collapsed to (0, 0) and disappeared. There was no fallback to the other source.

For reference, hyundai_kia_connect_api only uses /location/park when it actually contains coordinates (if … get_child_value(location, "coord.lat")) and otherwise keeps the live location.

Changes

  • parseLocation reads both candidate sources and falls back to whichever actually carries coordinates, only preferring /location/park when it is non-empty and at least as recent as the status location.
  • VehicleStatus.Location.hasCoordinates — new helper treating (0, 0) (null island) as "no fix".
  • getDoubleFromJson — fixed a force-unwrap (Double(value)!) that would crash the entire status parse on any non-numeric coordinate string.

Testing

  • swift build passes.
  • Legacy/CCS2 coordinate and date key paths verified against hyundai_kia_connect_api.

Note

This addresses the common "no recent park event" case where one source is empty. If a vehicle returns coordinates from neither source, a dedicated GET …/location call (as the reference makes) would be the follow-up — happy to add it if logs show that shape.

parseLocation chose the /location/park branch purely by date, so when
the park endpoint returns no coordinates (no recent park event) and the
status response carries no embedded vehicleLocation, the location
collapsed to (0, 0) and disappeared from the UI.

- parseLocation now reads both candidate sources (park endpoint and the
  status-embedded location) and falls back to whichever actually carries
  coordinates, only preferring park when it is non-empty and at least as
  recent as the status location.
- Add VehicleStatus.Location.hasCoordinates, treating (0, 0) null island
  as "no fix".
- Fix a force-unwrap in getDoubleFromJson (Double(value)!) that would
  crash the entire status parse on any non-numeric coordinate string.
Copilot AI review requested due to automatic review settings May 26, 2026 09:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR improves how vehicle coordinates are represented and selected when parsing Hyundai Europe API responses, reducing missing/stale location results and avoiding a potential crash when parsing numeric strings.

Changes:

  • Added VehicleStatus.Location.hasCoordinates to detect the API’s (0,0) “no fix” sentinel.
  • Updated location parsing to choose between park endpoint vs. status-embedded location based on recency and coordinate presence.
  • Made string-to-double parsing non-throwing to prevent runtime crashes.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
Sources/BetterBlueKit/Models/VehicleStatus.swift Adds a helper for determining whether a location contains real coordinates vs. (0,0) sentinel.
Sources/BetterBlueKit/API/HyundaiEurope/HyundaiEuropeAPIClient+Parsing.swift Refines location source selection logic and makes numeric parsing from strings safer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +281 to +290
let parkIsNewer = (parkDate != nil && locationDate != nil)
? locationDate!.compare(parkDate!).rawValue <= 0
: parkLocation.hasCoordinates
if parkIsNewer, parkLocation.hasCoordinates {
return parkLocation
}
if statusLocation.hasCoordinates {
return statusLocation
}
return parkLocation.hasCoordinates ? parkLocation : statusLocation
// whichever source actually carries coordinates so a missing park
// event no longer drops the location entirely.
let parkIsNewer = (parkDate != nil && locationDate != nil)
? locationDate!.compare(parkDate!).rawValue <= 0
// as recent as the (often stale) status location. Fall back to
// whichever source actually carries coordinates so a missing park
// event no longer drops the location entirely.
let parkIsNewer = (parkDate != nil && locationDate != nil)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd much rather use guard statements instead of != nil and then a force unwrapping.

@schmidtwmark

Copy link
Copy Markdown
Owner

This looks reasonable enough to me -- I had a comment on some of the parsing logic. I'd also like confirmation that this was run through bbcli with your vehicle

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.

3 participants