Skip to content

feat: multi-region support (fixes 1501 "geelyos verify error" outside the EU) - #3

Open
alexandrepossebom wants to merge 1 commit into
nitaybz:mainfrom
alexandrepossebom:feat/multi-region
Open

feat: multi-region support (fixes 1501 "geelyos verify error" outside the EU)#3
alexandrepossebom wants to merge 1 commit into
nitaybz:mainfrom
alexandrepossebom:feat/multi-region

Conversation

@alexandrepossebom

Copy link
Copy Markdown

Refs #1 — this fixes the NA region (and lays the table down for the rest); AU
and TH are still blocked on credentials nobody has extracted yet, so the issue
should stay open for them.

The problem

Every host and credential is hardcoded to the EU region (api/apis.ecloudeu.com,
m-lcmsam-eu.geely.com, app id GEELYE245). A car registered in another region
fails certificate provisioning with 1501 geelyos verify error, because the
cert server validates the token against the GeelyOS of its own region. That is
the error in #1, and it is not recoverable by retrying or by re-authenticating.

What I found

The region is not derivable from the account's country code. My test car is
a Geely EX2 bought in Brazil, on an account registered as BR, and it is
registered in the NA region. The authoritative source is the login response:

  • tspInfo[].serviceRegion — the vehicle's telematics region
  • edgeInfo.code — same value, at the top level

masterInfo.code is the identity centre (EU for my account) and is misleading;
using it is what sends a Brazilian car to the EU hosts.

I verified the failure is invariant: 1501 happens identically on the EU and SA
hosts, with either the raw token or an accessCode obtained from getCode. Only
pointing at the correct region (NA) provisions the certificate.

What this PR changes

  • const.py: a REGIONS table (EU and NA) plus region_config() and
    CONF_REGION. Each entry carries its app_id/app_secret and the three hosts
    that differ per region: cert (provisioning), control (mTLS commands) and app
    (getCode / vehicle list).
  • api.py: GeelyApi(region=...) derives hosts and credentials from that table;
    new region_from_login(); provision_user_cert() and list_vehicles() take
    the host explicitly. The request signer is renamed
    _sign_request_for_api_ecloudeu_sign_cert_request, since it was never
    EU-specific.
  • config_flow.py: derives the region from the login response, provisions
    against the right cert host, and stores CONF_REGION in the entry.
  • __init__.py: builds the client with the stored region.

Login (access-app-global.geely.com) and captcha are global and unchanged.

Compatibility

Existing EU installs keep working unchanged: the EU parameters are exactly the
current constants, and anything the login does not identify falls back to EU. No
entity IDs, unique IDs or config-entry keys change for them.

Verified

On a real car (EX2 MAX, model code E22H-G, NA region): region_from_login()
returns NA for the Brazilian car and EU for an EU account,
provision_user_cert() provisions a real certificate against the NA host
(cert/info and cert/file both return code 1000), and the integration polls
status and executes commands normally afterwards. @Nathan-Cunha independently
confirmed the same branch works on a Geely EX5 in Brazil (49 entities, HA
2026.7.4) — see his comment in #1.

Two things worth calling out

  1. app_id per region or per model? GEELYUS is clearly regional, but
    GEELYE245 is a model name (EX5). With only two data points (EU/EX5 and
    NA/EX2) I cannot separate the two hypotheses. This PR keys on region and says
    so in a comment. If you have a third data point, it settles the question.
  2. APAC and SA are stubs. Their hosts appear in the app's assets/host.txt
    (*.ecloudkr.com, m-lcmsam-kr, tsp-geely-api-sa.xcloudsvc.com) but I have
    no way to obtain their app id / secret, so they are commented out rather than
    guessed. That is why this is Refs #1 and not Fixes #1: the AU and TH
    reporters in that thread need those credentials, and anyone who can capture a
    login from those regions can fill the table in without touching any other
    code.

The NA credentials are in the table in the same form the EU ones already are in
this repo.

The integration hard-coded the EU backend (api/apis.ecloudeu.com,
m-lcmsam-eu.geely.com) and the EU app credentials (GEELYE245). Vehicles
registered in another region fail device-cert provisioning with
`1501 geelyos verify error`, because the cert server verifies the token
against that region's GeelyOS — see nitaybz#1.

Region is now derived from the login response (`tspInfo[].serviceRegion`
/ `edgeInfo.code`), NOT the country code: a Brazil account can have an
NA-region vehicle. A `REGIONS` table in const.py maps each region to its
hosts + app credentials, and the client / provisioning / vehicle-list
calls resolve hosts per-region. The chosen region is stored on the config
entry (CONF_REGION).

- const.py: add REGIONS table (EU + NA confirmed; APAC/SA hosts noted,
  credentials pending) + region_config() helper + CONF_REGION.
- api.py: GeelyApi takes `region` and derives cert/control/app hosts +
  app_id/secret; provision_user_cert takes cert_host; list_vehicles takes
  app_host; add region_from_login(); rename EU-specific signer.
- config_flow.py: derive + store region, provision on the right host.
- __init__.py: build the client with the stored region.

Verified end-to-end on a Brazil-market Geely EX2 (region NA, GEELYUS):
device cert provisions and read + control (find_car) work. EU users are
unaffected — their region resolves to EU with the same hosts/credentials
as before.
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.

1 participant