Brings your Omoda / Jaecoo car into ioBroker: vehicle status, GPS location, battery/charging, and remote lock and climate — using the same cloud backend as the official Omoda / Jaecoo app.
⚠️ Unofficial, reverse-engineered software. Not affiliated with, endorsed by, or connected to Omoda, Jaecoo or Chery. Provided "as is", use at your own risk and only on your own vehicle. Names and trademarks belong to their respective owners. See the LICENSE.
- Status — doors, windows, sunroof, lock, engine, climate running, tyre pressures/temperatures.
- Location — GPS latitude/longitude, speed, heading.
- Battery & charging — state of charge, electric/total range, charge state, charging power, charge plug connected, remaining charge time.
- Fuel (hybrids / combustion only) — fuel remaining in litres, petrol range, average fuel consumption, low-fuel warning. The car reports litres, not a fuel-level percentage.
- Units — ranges, odometer and speed in kilometres or miles (setting "Distance unit" on the "Region & polling" tab; default km).
- Commands — lock/unlock, climate on/off with a settable target temperature, request GPS location, find car (flash lights), and "wake & refresh full status".
Additional functions from the vehicle app (individual seat heating/ventilation, defrosters, EV charge start/stop and scheduled charging, windows/sunroof/trunk control, theft alarm) are planned for a later version.
- Node.js >= 22, js-controller >= 6.0.11 and admin >= 7.8.23.
- An Omoda / Jaecoo account with the vehicle associated (owner or delegated access).
- The account email and command PIN.
- Region: defaults to Europe (also confirmed working in the UK). Other markets can be configured under Region & polling in the adapter settings.
Tip: consider using a delegated second account for the adapter. Logging in with the same account here and in the official app makes the two repeatedly log each other out.
- Install the Omoda / Jaecoo adapter from the ioBroker admin Adapters tab and create an instance.
- On the Account tab, enter your email and command PIN and save.
- On the Login (OTP) tab, press Request OTP code — a one-time code is emailed to you.
- Enter the code and press Confirm OTP. The adapter logs in and discovers your vehicle(s),
creating a device per VIN under
omoda.0.<VIN>.
A new OTP is only needed if the session later expires (typically because the official app was opened); otherwise the session is refreshed automatically.
- The command PIN is your account PIN. Entering a wrong PIN repeatedly can lock the account, so the adapter stops after a couple of failed attempts — re-check the PIN in the settings before retrying.
- Many values read
nullwhile the car is in standby; battery, speed and mileage update while driving or charging, or after pressing Refresh full status (which briefly wakes the car). - Waking the car is rate-limited by the backend, so the adapter enforces a cooldown.
- The MQTT telemetry connection uses mutual TLS. The client certificate/key material and the
pinned CA ship with the adapter in
data/certs-store.json(encrypted, as recovered by the upstream HA integration) so the adapter works offline without a provisioning round-trip. If Chery ever rotates the MQTT CA or client certs, that file has to be regenerated and a new adapter version released — telemetry will fail to connect until then.
This adapter is a port of the excellent reverse-engineering work in the Home Assistant integration omoda-jaecoo-ha by Caslinovich and JackRonan. The protocol constants, request-signing formulas, MQTT credential derivation, and endpoint recipes were recovered by them and are used here under the MIT License — this project would not exist without their effort. Please star and support the upstream project. All bugs in this ioBroker port are mine, not theirs. Development of the integration has since moved to chery-connect-ha/omoda9-ha.
- (Alan Paris) Added fuel states for hybrid/combustion cars:
fuel.remaining(L),fuel.range(km),fuel.averageConsumption(L/100 km) andfuel.lowWarning. They are not created on a pure-electric car - (Alan Paris) Added
commands.findCar— flashes the lights so you can find the car - (Alan Paris) New "Distance unit" setting: show ranges, odometer and speed in miles / mph instead of km / km/h. Existing history keeps the unit it was recorded in
- (Alan Paris) Fixed the battery level and electric range jumping to wrong values (e.g. 97% instead of 82%, 0 km) while the car is parked with the high voltage off: such placeholder readings are now ignored and the last real values are kept
- (Alan Paris) Fixed
battery.rangeTotaldropping by the whole petrol range when a reading did not include it; on a pure-electric car it is now the electric range alone - (Alan Paris)
charging.poweris now cleared when charging ends instead of keeping the last value - (Alan Paris) Fixed
charging.stateshowing a raw number like1.0instead of "Charging" - (Alan Paris)
info.lastUpdatenow changes only when the car's data actually changes, so a parked car's cached data no longer looks fresh on every poll - (Alan Paris) A command refused because the car is busy, asleep, out of wake requests or not permitted no longer counts as a wrong PIN — before, two such refusals blocked commands and asked you to re-enter a correct PIN (upstream 46ab54e)
- (Alan Paris) A refresh token the server has rejected is no longer resent on every poll; it is retried at most hourly until a new OTP is entered (upstream 3c7ced3)
- (Alan Paris) The session is renewed silently when the login reply has no user token, instead of asking for a new OTP (upstream 92a57b3)
- (Alan Paris) The access token is renewed before it expires, not after (upstream 3c7ced3)
- (Alan Paris) A network failure during token renewal is reported as a network error, not as "request a new OTP"; login failures now log the HTTP status, error code and region
- (Alan Paris) Climate run time now uses one of the durations the car allows (
maxAirDurationis a set such as "5,10,15", not a maximum) instead of always sending 15 minutes (upstream 97b3edf) - (Alan Paris) An implausible climate temperature range from the backend is now ignored instead of applied, and the target temperature is clamped into the car's range before a climate ON is sent (upstream ac605fa)
- (Alan Paris) A command answered with A00567 (taskId invalid) now re-mints the taskId and retries once, like A00089/A00546
- (Alan Paris) Requires admin >= 7.8.23 now
- (Alan Paris) Tested with Node.js 26; updated @iobroker/testing to 6.x
- (Alan Paris) New adapter icon (OMODA | JAECOO lockup) replacing the template placeholder
- (Alan Paris) BREAKING:
commands.locknow follows the ioBrokerswitch.lockspec — true UNLOCKS the car, false locks it. Any script writingtrueto lock must be inverted. This is the polarity Alexa/Google (via ioBroker.iot), VIS lock widgets and type-detector already assume, so the old behaviour unlocked the car when the user asked to lock it - (Alan Paris) Fixed telemetry fields the car reports as
nullbeing stored as real values:doors.lockedno longer reads "locked" when nothing was reported, and GPS no longer jumps to latitude/longitude 0 - (Alan Paris)
info.connectionnow returns to false when the session expires (e.g. the official app logs in on the same account) instead of staying true while every poll silently failed - (Alan Paris) Corrected the "Dept ID" help text in all 11 languages — the default is 44 (UK), not 39
- (Alan Paris) The OTP code is now encrypted at rest, not only marked protected
- (Alan Paris)
location.headinguses a plainvaluerole (value.directionis an up/down/opening enum, not a compass bearing) - (Alan Paris) Car MQTT reconnects back off from 10 s to 2 min and stop repeating the same warning, instead of retrying every 10 s forever when the broker keeps rejecting us
- (Alan Paris) The captcha solver yields to the event loop, so it can no longer stall other adapters sharing a compact host process
- (Alan Paris) Removed unused constants, token-store helpers and the placeholder test file
- (Alan Paris) Default "Dept ID" is now 44 (UK) instead of 39 (IT) — existing instances keep the value they already have (upstream 37f8f2b)
- (Alan Paris) Fixed a stale "charge remaining time": the field vanishes from the payload when charging ends, so the state now clears instead of showing the last value for hours (upstream a0f61ed)
- (Alan Paris) Command confirmations no longer cry "check failed" when the car reports only the climate module — the backend includes it on nearly every successful OFF command (upstream 2cc7d56)
- (Alan Paris) Backend rejections caused by vehicle permissions or a malformed request (A00374/A00554/A00567/A00604/A00643/A00757) no longer count towards the wrong-PIN anti-lockout (upstream 8aa4176)
- (Alan Paris) Correct state roles for info.model and info.brand; account email marked as protected
- (Alan Paris) The charging/driving fast-follow poll now self-schedules, so a slow probe can no longer overlap the next one
- (Alan Paris) Security: never log the VIN or MQTT user id in cleartext (masked to a short suffix)
- (Alan Paris) Fixed a leak of the car MQTT client and polling timers when re-logging in after a session drop
- (Alan Paris) Added credit to the upstream Home Assistant integration (JackRonan/omoda-jaecoo-ha) in the README and LICENSE
- (Alan Paris) Added full admin UI translations for all 11 languages
- (Alan Paris) Config: clearer "Dept ID" help — it must match your account country's dialing code (UK=44, IT=39, …)
- (Alan Paris) Require Node.js >= 22 and admin >= 7.6.17; enabled automated npm publishing via trusted publishing (OIDC)
- (Alan Paris) Marked the OTP code as protected; various adapter-checker compliance fixes
- (Alan Paris) initial release
Older changelogs can be found there
MIT License
Copyright (c) 2026 Alan Paris alan.paris@scottish.rugby
Portions Copyright (c) 2026 Caslinovich and the omoda-jaecoo-ha contributors (JackRonan), ported from https://github.com/JackRonan/omoda-jaecoo-ha (MIT) and retained under its terms.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
