<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tanguy</title>
    <description>The latest articles on DEV Community by Tanguy (@tanguymossion).</description>
    <link>https://dev.to/tanguymossion</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3966637%2F36a774a2-0edb-4e00-9cb9-9136dd5b3515.jpeg</url>
      <title>DEV Community: Tanguy</title>
      <link>https://dev.to/tanguymossion</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tanguymossion"/>
    <language>en</language>
    <item>
      <title>Building the first Dart/Flutter client for MG/SAIC connected vehicles: where AI did more than write code</title>
      <dc:creator>Tanguy</dc:creator>
      <pubDate>Thu, 04 Jun 2026 07:24:53 +0000</pubDate>
      <link>https://dev.to/tanguymossion/building-the-first-dartflutter-client-for-mgsaic-connected-vehicles-where-ai-did-more-than-write-3anc</link>
      <guid>https://dev.to/tanguymossion/building-the-first-dartflutter-client-for-mgsaic-connected-vehicles-where-ai-did-more-than-write-3anc</guid>
      <description>&lt;p&gt;Last weekend, I unlocked my car using my own Dart package (according to pub.dev, the first one ever built for it). I walked down to the parking lot, opened the app, tapped the button, heard the clunk. "Worth it." Then I locked it again, went back upstairs, and called it a productive Sunday.&lt;/p&gt;

&lt;p&gt;But let's get back a few months earlier, when I had bought a connected MG. I opened the official app, and like any developer who gets a new piece of technology, I spent about five minutes enjoying it as a normal person. Then I started wondering how it worked, what APIs were behind the app, and whether I could build something on top of it myself.&lt;/p&gt;

&lt;p&gt;A quick search confirmed that no Dart package existed for the SAIC iSmart API. That's how I ended up building the first one.&lt;/p&gt;

&lt;p&gt;My initial approach was to decompile the official app.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the official app reveals, and what it doesn't
&lt;/h2&gt;

&lt;p&gt;I extracted the APK directly from my phone via &lt;code&gt;adb&lt;/code&gt; and decompiled it with &lt;code&gt;apktool&lt;/code&gt;. The official iSmart app is a native Kotlin Android app.&lt;/p&gt;

&lt;p&gt;Most of the business logic is protected by ijiami encryption, so the core algorithms aren't readable statically. From the unprotected resources (strings, drawables, layout files) I was able to confirm a handful of things: undocumented features, error codes, model naming conventions. Useful context, but not enough. The actual API protocol wasn't there. It lives in the encrypted DEX (the compiled Android bytecode), and I had hit a wall.&lt;/p&gt;




&lt;h2&gt;
  
  
  The community that got there first
&lt;/h2&gt;

&lt;p&gt;I wasn't the first to hit that wall. A GitHub organization called &lt;a href="https://github.com/SAIC-iSmart-API" rel="noopener noreferrer"&gt;SAIC-iSmart-API&lt;/a&gt; has been reverse-engineering the SAIC API for years via traffic capture. They have a Python client, a Java client, a Home Assistant integration, an MQTT gateway, and even a &lt;a href="https://github.com/SAIC-iSmart-API/documentation" rel="noopener noreferrer"&gt;dedicated documentation repo&lt;/a&gt; with real captured API responses.&lt;/p&gt;

&lt;p&gt;What wasn't covered was mobile-first development. No Dart client, no Swift client, no TypeScript package designed for mobile development. The Java client exists but is built for server-side use (Maven, MQTT gateways, Spring). A workaround exists: run the Python client on a server and use it as a proxy. But a mobile app should ideally call the API directly.&lt;/p&gt;

&lt;p&gt;So, as a Flutter developer, I built &lt;code&gt;saic_ismart&lt;/code&gt;: the first pure Dart package for the SAIC iSmart API. MG, Roewe, Maxus/LDV: brands that together represent millions of connected vehicles across Europe, Australia, India, and beyond.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the protocol
&lt;/h2&gt;

&lt;p&gt;The SAIC API isn't officially documented. There's no Swagger file, no official SDK. Everything I know about it came from reading the Python and Java clients line by line (well, Claude did most of the reading), cross-referenced with the community documentation repo.&lt;/p&gt;

&lt;p&gt;Here's what's actually going on under the hood.&lt;/p&gt;

&lt;p&gt;The SAIC API isn't REST. It's encrypted REST, with AES-128-CBC on every request, HMAC-SHA-256 signatures, and an event-id polling loop for asynchronous commands. Non-trivial to implement, but the Python client had a test vector. Matching it exactly in Dart was the first real milestone.&lt;/p&gt;

&lt;p&gt;A few other things worth knowing before you use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sessions are single-tenant: using the client disconnects the official app for ~900 seconds&lt;/li&gt;
&lt;li&gt;The VIN is never sent raw, always &lt;code&gt;sha256(vin)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The login uses a hardcoded credential that decodes to &lt;code&gt;sword:sword_secret&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of these quirks are just amusing. Others are real constraints. The package surfaces them clearly so you know exactly what you're dealing with.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building it with AI assistance
&lt;/h2&gt;

&lt;p&gt;Claude Code was part of my stack on this one, and it made a real difference. Not just for code generation.&lt;/p&gt;

&lt;p&gt;Before writing a single line of Dart, I had Claude analyze both the Python and Java repos and produce a concrete technical spec: authentication flow, encryption pipeline, endpoint behavior, known quirks. What would have taken days of reading became a structured document in a fraction of the time. That's how &lt;code&gt;TECHNICAL_REFERENCE.md&lt;/code&gt; was born, not as an afterthought but as the foundation the implementation was built on.&lt;/p&gt;

&lt;p&gt;Claude also helped port the AES-128-CBC crypto pipeline from Python to Dart and write the HMAC-SHA-256 implementation with the right key derivation. Technically straightforward once you know what to do, but tedious to get right.&lt;/p&gt;

&lt;p&gt;There's a limit to what AI can do here though. No language model can plug a phone into a car and tell you what the raw values actually mean in the real world. For that, I had to test on my MG3 EU. &lt;em&gt;(Though technically, an MCP integration could have automated that too. Maybe next time.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What the car actually tells you
&lt;/h2&gt;

&lt;p&gt;The Python client leaves some fields uninterpreted. So I ran the code, looked at the numbers, and reasoned about them. A few examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Raw value&lt;/th&gt;
&lt;th&gt;Unit&lt;/th&gt;
&lt;th&gt;Converted&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mileage&lt;/td&gt;
&lt;td&gt;&lt;code&gt;243790&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;decameters&lt;/td&gt;
&lt;td&gt;24 379 km&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tyre pressure (FL)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;61&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PSI×2&lt;/td&gt;
&lt;td&gt;30.5 PSI / 2.1 bar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tyre pressure (RR)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;70&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PSI×2&lt;/td&gt;
&lt;td&gt;35 PSI / 2.4 bar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Many other fields follow similar patterns: sentinel values, unexpected scales, units that only make sense once you cross-reference with the real world. All of these field experiments enriched the &lt;code&gt;TECHNICAL_REFERENCE.md&lt;/code&gt;, turning it from a protocol spec into something that actually reflects how the API behaves on a real vehicle.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you can do with it
&lt;/h2&gt;

&lt;p&gt;Here's what the package actually lets you do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lock and unlock&lt;/strong&gt; your vehicle remotely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start or stop climate control&lt;/strong&gt; (A/C, heating, fan-only, defrost)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control heated seats&lt;/strong&gt; with level support (off / low / medium / high)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor tyre pressure&lt;/strong&gt; with per-wheel values in bar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get GPS location&lt;/strong&gt; and vehicle status in real time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find my car&lt;/strong&gt; by triggering horn and lights remotely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open the tailgate&lt;/strong&gt; remotely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detect vehicle features&lt;/strong&gt;: does your car have a sunroof? Heated seats? TPMS? The package reads &lt;code&gt;vehicleModelConfiguration&lt;/code&gt; and exposes 23 typed getters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few lines of setup, then straight to the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SaicClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;config:&lt;/span&gt; &lt;span class="n"&gt;SaicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;username:&lt;/span&gt; &lt;span class="s"&gt;'you@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;password:&lt;/span&gt; &lt;span class="s"&gt;'yourpassword'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;region:&lt;/span&gt; &lt;span class="n"&gt;SaicRegion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;europe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;vehicles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getVehicles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;vin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vehicles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Status&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getVehicleStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;basicVehicleStatus&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;mileageKm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                &lt;span class="c1"&gt;// 24447.0&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;basicVehicleStatus&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;lockState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                &lt;span class="c1"&gt;// LockStatus.locked&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;basicVehicleStatus&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;frontLeftTyrePressureBar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 2.48&lt;/span&gt;

&lt;span class="c1"&gt;// Remote commands&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lockVehicle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startClimate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;temperatureIndex:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;controlHeatedSeats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;driverLevel:&lt;/span&gt; &lt;span class="n"&gt;HeatLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findMyCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All API calls are serialized. The SAIC server doesn't support concurrent requests on the same session.&lt;/p&gt;

&lt;p&gt;The first time I unlocked my car from my own Flutter app, not from iSmart, not from a terminal, but from a UI I had built on top of code I had written, was a genuinely satisfying moment. And it kept happening: the first successful &lt;code&gt;findMyCar()&lt;/code&gt; triggering the horn from across the street, the first time heated seats started before I even got to the car. Each feature that went from "POST /vehicle/control" to a real physical reaction felt like a small proof of concept becoming real.&lt;/p&gt;




&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;452 tests. 160/160 on pub.dev. 23 vehicle feature detection getters. Tested in production on a real MG3 EU. Pure Dart, no native code, works in any Flutter or Dart project.&lt;/p&gt;

&lt;p&gt;The package is on pub.dev: &lt;a href="https://pub.dev/packages/saic_ismart" rel="noopener noreferrer"&gt;saic_ismart&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;EV features like battery SoC, charging management, and climate scheduling are the natural next milestone. I don't own an EV myself, so I can't develop or validate those features without the right hardware. I would be genuinely happy to welcome contributors who own an MG4, ZS EV, or Roewe. The architecture is in place and ready to be extended. Open an issue on GitHub.&lt;/p&gt;

&lt;p&gt;The client is pure Dart, so wherever Flutter runs, this can run too. The package is a foundation. Build what's missing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with significant help from the research work of the &lt;a href="https://github.com/SAIC-iSmart-API" rel="noopener noreferrer"&gt;SAIC-iSmart-API&lt;/a&gt; community. Their work greatly accelerated my development.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://pub.dev/packages/saic_ismart" rel="noopener noreferrer"&gt;pub.dev&lt;/a&gt; · &lt;a href="https://github.com/tanguymossion/saic_ismart" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://tanguymossion.github.io/saic_ismart/" rel="noopener noreferrer"&gt;dart doc&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>ai</category>
      <category>opensource</category>
      <category>mobile</category>
    </item>
  </channel>
</rss>
