Fix Hyundai Europe commands on legacy (non-CCS2) vehicles#32
Open
dz0ny wants to merge 1 commit into
Open
Conversation
EU commands authenticate differently by generation. CCS2 (Gen5W) cars use a PIN-derived control token and the /api/v2/.../ccs2/control/* endpoints; legacy cars use the normal access token and the /api/v1/.../control/* endpoints with no PIN step at all (per hyundai_kia_connect_api's ApiImplType1). sendCommand previously called setCommandToken() unconditionally, so legacy vehicles failed with "Failed to get command token" — the PIN endpoint isn't part of their flow. It also built a malformed path because commandPathAndBody() defaulted to ccs2: true while the URL used v1. This matches the "wrong token usage on legacy command path" issue noted in schmidtwmark#8. - sendCommand: branch on ccs2 — control token + commandHeaders for CCS2, plain authorizedHeaders (no PIN) for legacy; pass the real ccs2 flag into commandPathAndBody. - commandPathAndBody: drop stray leading slash in the legacy door path ("/control/door" -> "control/door") that produced a double slash. - setCommandToken: route through performJSONRequest so the request is logged and status-validated, and surface a clearer PIN-failure error. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates the Hyundai Europe command flow to properly distinguish CCS2 (PIN/control-token) vehicles from legacy vehicles, and improves diagnostics by routing PIN verification through the shared request pipeline.
Changes:
- Route the PIN/control-token request through
performJSONRequestfor consistent logging/status validation. - Only fetch a PIN-derived control token for CCS2 vehicles; use normal auth headers for legacy vehicles.
- Fix legacy command paths to avoid double slashes in constructed URLs.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| Sources/BetterBlueKit/API/HyundaiEurope/HyundaiEuropeAPIClient.swift | Reworks PIN/control-token fetching and command header selection based on CCS2 support; improves error messaging and logging consistency. |
| Sources/BetterBlueKit/API/HyundaiEurope/HyundaiEuropeAPIClient+Commands.swift | Normalizes legacy command paths (removes leading /) to build correct request URLs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+202
to
+203
| let body: [String: Any] = [ | ||
| "deviceId": configuration.deviceId ?? "", |
Comment on lines
+222
to
+223
| throw APIError( | ||
| message: "PIN verification failed — check that the account PIN is correct.", |
Owner
|
This looks okay to me -- the AI comments are pretty sensible and should be addressed. Have you confirmed this using |
schmidtwmark
pushed a commit
that referenced
this pull request
May 28, 2026
Mirrors the three legacy-path bugs Nachtlatscher fixed for Hyundai EU in #32 — flagged in the #29 thread after merge. - Drop leading slash on lock/unlock door paths so the URL builder doesn't produce a double slash. - Stop using the PIN-derived control token on legacy v1 endpoints. Legacy endpoints reject control-token auth with "400 Authorization field missing" and expect the regular access token. Gate the control-token flow on ccs2 and use authorizedHeaders for the legacy branch. - Pass ccs2: ccs2 to commandPathAndBody. The call site relied on the default ccs2: true, so legacy vehicles got the CCS2 payload shape against a v1 URL. Live-validated against a CCS2 EV9 by hard-coding ccs2 = false locally: lock + unlock both return 200 / resCode 0000 and the doors physically lock and unlock — confirming the Bluelink backend routes legacy v1 and CCS2 endpoints to the same handler. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Sending commands (lock/unlock/charge) to legacy, non-CCS2 Hyundai Europe vehicles (e.g. a gen2 Bayon) fails with "Failed to get command token" /
Lock … failed.Root cause
EU commands authenticate differently depending on vehicle generation, per the authoritative
hyundai_kia_connect_api(ApiImplType1.lock_action/start_charge/ …):POST /api/v1/spa/…/control/doorPOST /api/v2/spa/…/ccs2/control/door{action, deviceId}{command}sendCommandcalledsetCommandToken()(thePUT /api/v1/user/pinstep) unconditionally, but legacy cars have no PIN/control-token step — so that fetch is what threw "Failed to get command token". It also built a malformed path becausecommandPathAndBody()defaulted toccs2: truewhile the URL was built asv1.This is the "wrong token usage on legacy command path" anomaly noted in #8.
Changes
sendCommand: branch onccs2— control token +commandHeadersfor CCS2; plainauthorizedHeaders(no PIN) for legacy. Pass the realccs2flag intocommandPathAndBody.commandPathAndBody: drop the stray leading slash in the legacy door path ("/control/door"→"control/door") that produced a double slash.setCommandToken: route throughperformJSONRequestso the PIN request is captured in HTTP logs and status-validated (previously a rawURLSessioncall, invisible to diagnostics), with a clearer PIN-failure message.Testing
swift buildpasses.hyundai_kia_connect_apiApiImplType1./api/v1/spa/…/control/doorand return 200.Known remaining gap (not addressed here)
startClimatestill always returns theccs2/control/temperaturepath regardless of the flag; legacy climate-start needs a dedicatedcontrol/temperaturebranch with the reference's hvac payload.Refs #8
🤖 Generated with Claude Code