Contexte

Les machines a cafe connectees De’Longhi (gamme ECAM) utilisent le cloud Ayla Networks pour la communication WiFi et le Bluetooth Low Energy pour le controle local. Aucune documentation publique n’existe sur le protocole. L’application officielle De’Longhi Coffee Link est la seule interface disponible.

La machine cible : une De’Longhi Eletta Explore ECAM 450.65.S, connectee en WiFi au reseau local, avec un firmware ADA 1.6 base sur esp-idf v3.3.1 (ESP32).

L’objectif : creer une integration Home Assistant pour controler la machine sans dependre de l’app officielle.

Methodologie

1. Decompilation de l’APK Android

L’application De’Longhi Coffee Link pour Android a ete decompilee avec jadx. L’analyse du code Java/Kotlin a revele :

  • Le format de commande brew utilise le prefixe 0x83 avec des paires parametre ID/valeur
  • Le flux d’authentification : SSO via Gigya (SAP CDC) → token JWT → echange contre un token Ayla
  • La structure des messages BLE et la couche de chiffrement AES
  • Les endpoints API cloud : ads-eu.aylanetworks.com (EU) et ads-field.aylanetworks.com (US)

Pour contourner le certificate pinning, l’APK a ete patchee avec apktool et resignee. Cela a permis l’interception HTTPS.

2. Interception du trafic cloud

En configurant mitmproxy avec un certificat custom installe sur un appareil de test, j’ai capture le flux d’authentification complet :

  1. Authentification Gigya : POST accounts.eu1.gigya.com/accounts.login → retourne un id_token JWT
  2. Echange de token : POST user-field-eu.aylanetworks.com/api/v1/token_sign_in.json avec le JWT → retourne un access_token Ayla
  3. Listing des devices : GET ads-eu.aylanetworks.com/apiv1/devices.json → retourne le DSN (Device Serial Number) et les proprietes
  4. Envoi de commandes : POST ads-eu.aylanetworks.com/apiv1/dsns/{DSN}/properties/app_data_request/datapoints.json

Le format de chaque commande WiFi est :

[ECAM_bytes] + [4 bytes timestamp big-endian (Unix)] + [4 bytes app_id: 0x20 0x40 0x35 0xEF]

Le tout encode en Base64 et envoye comme valeur du datapoint.

3. Reverse du format de commande 0x83

Le format de commande brew a ete le plus complexe a reverser. Il a fallu croiser la decompilation jadx avec 9 captures MITM de differentes boissons pour comprendre la conversion recette → brew :

Format Recipe (0xA6) — stocke dans l’app :

[0xD0][len][0xA6][0xF0][profile][bev_id][param_pairs][CRC-16]

Format Brew (0x83) — envoye a la machine :

[0x0D][len][0x83][0xF0][bev_id][action][param_pairs][profile_save][CRC-16]

Les parametres sont encodes en paires ID/valeur :

  • 8-bit : [param_id][value] (2 octets) — ex: intensite, temperature
  • 16-bit : [param_id][value_hi][value_lo] (3 octets) — pour COFFEE(1), MILK(9), HOT_WATER(15)

Regles de conversion decodees :

  1. Extraire le bev_id de la recette
  2. Parser les param pairs
  3. Exclure le parametre VISIBLE(25)
  4. Pour les boissons glacees : exclure COFFEE/MILK/HOT_WATER, ajouter ICED(31)
  5. Ajouter RINSE(39)=1
  6. CRC : CRC-16/SPI-FUJITSU

La conversion a ete validee byte-for-byte sur 9 captures MITM : espresso, hot_water, tea, iced americano, iced cappuccino, cold brew original, cold brew intense, cold brew to mix, americano froid.

4. Analyse BLE

Avec nRF Connect sur Android, j’ai capture les echanges BLE entre l’app et la machine. Les commandes suivent un format structure avec :

  • Un header de commande (type + longueur)
  • Le payload chiffre en AES-256-CBC
  • Un key exchange initial sur le port 6682 pour le mode LAN

Le protocole BLE/LAN n’est pas encore implemente dans l’integration (en cours de developpement).

Resultats

Integration Home Assistant

L’integration finale expose :

TypeNombreExemples
Capteurs30Temperature, compteur cafes, progression detartrage, filtre
Capteurs binaires19Plateau plein, reservoir vide, porte ouverte, module lait absent
Boutons50+Un par boisson, auto-decouverts depuis la machine
Switch1Power on/off

Chaque commande brew inclut des pre-brew safety checks : machine eteinte/occupee/en detartrage, reservoir vide, bac a marc plein, grains vides, plateau absent, accessoire lait manquant pour les boissons au lait.

Compatibilite

Testee et validee par la communaute sur :

  • De’Longhi Eletta Explore ECAM 450.65.S
  • De’Longhi PrimaDonna Soul
  • Plusieurs autres modeles ECAM (retours utilisateurs en cours)

L’integration supporte les regions EU et US (endpoints Ayla differents), et est traduite en 16 langues.

Communaute & Contributeurs

Ce projet n’aurait pas atteint ce niveau sans les retours et tests de la communaute :

ContributeurContribution
MattG-KDocumentation protocole independante (dlghiot) — algorithme CRC-16/SPI-FUJITSU, table de request IDs, commande standby. A economise des heures de reverse.
silvanverschuurPremier testeur externe — a valide power on/off sur Eletta Explore, confirme la reactivite superieure a l’app officielle (#1)
geekofweekA expose le probleme multi-region en capturant les appels DNS depuis l’app officielle — a mene au support US (#2)
agarthandTests sur PrimaDonna Soul, retours compteurs manquants (#3), signalement rate limiting cloud (#9)
jostrasserA aide a decouvrir les noms de proprietes legacy (data_request vs app_data_request) sur PrimaDonna Soul (#5)

Publication

Le code est publie en open-source :

Outils utilises

OutilUsageLien
jadxDecompilation APK Androidgithub.com/skylot/jadx
apktoolPatch APK (bypass cert pinning)apktool.org
mitmproxyInterception HTTPSmitmproxy.org
nRF ConnectAnalyse BLEnordicsemi.com
WiresharkAnalyse de paquetswireshark.org
PythonIntegration Home Assistantpython.org
CRC RevEngIdentification CRC-16reveng.sourceforge.io

References