Hack de ATAG One App – deel 2

De ATAG One app (voor iPhone en Android) benadert rechtstreeks via het wifi netwerk de ATAG One thermostaat. Atag stelt helaas geen technische documentatie beschikbaar hoe de thermostaat aan te sturen is. De enige manier om uit te vissen hoe de app “praat” met de thermostaat, is door de app te hacken 😀 😈

In deel 1 beschrijf ik hoe de app de ATAG One thermostaat in het netwerk vindt.

In deel 2 probeer ik te achterhalen hoe de app de thermostaat aanstuurt.

Communicatie met de thermostaat

In deel 1 is te lezen hoe de Android app uitgepakt kan worden. De meeste code is goed leesbaar. De naamgeving van de methodes spreken voor zich. De manier om de werking te achterhalen is om door de applicatie te bladeren en de code te begrijpen.

In het bestand ControlDeviceIndicatorC bevindt zich een functie met de naam switchToDeviceIp. Deze functie stelt op basis van het IP adres van de thermostaat de URL voor communicatie samen. In onderstaande code fragment zie je dat de thermostaat HTTP verbinding accepteert op poort 10.000.

switchToDeviceIp = function (broadcastingDevice) {
    isLocal = true;
    this.updateIcon();
    
    var deviceIp = broadcastingDevice.deviceIp;
    deviceUrl = "http://" + deviceIp + ":10000/";
    ultraLight.serverConnection.homeURL = deviceUrl;
}

In index.html staat een commentaar over pairing. Blijkbaar moet er eerst pairing tussen de thermostaat en het aansturende apparaat plaatsvinden. Handig dat de betekenis van de response codes er bij staat.

/* WIFI SETUP & PAIRING */

// If the devicequery was for pair_message, 
// and the response was not 2 for the status(1 = waiting, 2 = success, 3 = failed)
// then ask the device again
if (this.queryURL == 'pair_message') {
    var pairReply = getModelValue('m:pairReply.acc_status');

De inhoud van het pair bericht zier er als volgt uit:



<div id="skipselectYourOneSkippedLogInButton" data-query-url="pair_message" data-post='{"pair_message":{"seqnr":0,"accounts":{"entries":[{"user_account":"","mac_address":"m:deviceIdentifier","device_name":"m:deviceName","account_type":0}]}}}' data-target='{"m:pairReply":"pair_reply"}' data-set-showpage="confirmOnePage" data-set-failpage="selectYourOnePage" data-wifi-lastStep="searchForOnePage" data-pair-success="confirmedPairSuccess" data-transition="slide" data-no-error="true" data-show-overlay="true">
</div>


In de generieke Controller.sendRequest() staat nog de volgende code. Aan elk bericht dat de app verzendt, wordt blijkbaar de inlognaam en het MAC-adres van de afzender geplakt:

// Insert the account_auth message for the device, since it needs it
// Insert a different string depending if the device is coupled
if ($model("lastUsedAccount").get()!=null) {
    authMessage = ',"account_auth":"user_account":"m:lastUsedAccount","mac_address":"m:deviceIdentifier"}';
} else {
    authMessage = ',"account_auth":{"user_account":"","mac_address":"m:deviceIdentifier"}';
}
            
// Insert the string into post
var postInsertPosition = postString.indexOf(',');
var newPost = [postString.slice(0, postInsertPosition), authMessage, postString.slice(postInsertPosition)].join('');

Verzonden JSON:

{
  "pair_message": {
    "seqnr": 0,
    "accounts": {
      "entries": [
        {
          "user_account": "",
          "mac_address": "72:00:06:c0:78:e3",
          "device_name": "Test2!",
          "account_type": 0
        }
      ]
    }
  }
}
Screenshot van “Advanced Rest Client” pair request in Chrome
RestClientChrome-PairResponse
Screenshot “Advanced Rest Client” pair response in Chrome

Response in JSON (Na succes):

{
  "pair_reply": {
    "seqnr": 0,
    "acc_status": 2
  }
}
Bovenstaande Pair request levert de volgende vraag op de thermostaat op. “Test! probeert verbinding tot stand te brengen. Toestaan?”.

HTTP netwerk verkeer

Zoals hierboven getoond, gebruikt de App plain HTTP met JSON content om met de app te communiceren. De verbinding is lokaal niet versleuteld. Door nu een proxy tussen de app en de thermostaat te plaatsen, ga ik al het verkeer afluisteren.

Om verkeer af te luisteren, installeer ik een proxy met de naam Charles.

Charles is a web proxy (HTTP Proxy / HTTP Monitor) that runs on your own computer. Your web browser (or any other Internet application) is then configured to access the Internet through Charles, and Charles is then able to record and display for you all of the data that is sent and received.

HTTP Request onderscheppen via Proxy

Op de telefoon stel ik mijn computer’s IP adres in als Proxy met poort 8888. Via de app stel ik daarna de temperatuur in op 21 graden en kijk welk bericht de app verstuurt naar de thermostaat. Zie het resultaat hieronder.

ATAG One app via proxy, post temperature

{
  "update_message": {
    "seqnr": 0,
    "account_auth": {
      "user_account": "email@gmail.com",
      "mac_address": "3AA90444-401E-8978-B35A43809XXX"
    },
    "device": null,
    "status": null,
    "report": null,
    "configuration": null,
    "schedules": null,
    "control": {
      "ch_mode_temp": 21
    }
  }
}

Samengevat moeten we authentication gegevens account_auth en een control object control waarin de gewenste temperatuur staat als een POST request versturen naar de thermostaat.

Samenvatting

Eenmalig moet er een pair_request uitgevoerd worden om met de thermostaat te kunnen praten. Op basis van een unieke code mac_address onthoudt de thermostaat welke app toegang tot de thermostaat heeft.

Daarna kan de temperatuur ingesteld worden door een POST request te sturen naar het adres van de thermostaat op poort 10.000.

het ATAG One API project maakt gebruik van deze kennis om de thermostaat rechtstreeks uit te lezen aan aan te sturen. Zie ook de project wiki voor een technische beschrijving van de berichten (in het Engels).

3 thoughts on “Hack de ATAG One App – deel 2”

  1. Hoi,

    Ik ben bezig geweest om hetgeen jij in Java hebt geschreven te reproduceren in Perl. Nu loop ik tegen het probleem aan dat wanneer ik een retrieve_request stuur, ik altijd hetzelfde antwoord krijg en dit is niet het antwoord waar ik op hoopte 😉
    Mijn JSON post naar http://172.20.41.160:10000/retrieve ziet er als volgt uit:
    { “retrieve_message”: { “seqnr”: 0, “device_id”: “6808-1401-3108_15-12-001-005”, “info”: 18 } }
    De response die ik altijd krijg is:
    { “retrieve_reply”:{ “seqnr”:0,”acc_status”:0} }
    Het apparaat is gepaired.
    Ik heb ook de volgende post geprobeerd:
    {“seqnr”:1,”account_auth”:{“user_account”:””,”mac_address”:”6808-1401-3108_15-12-001-005″},”info”:15}}
    Maar dan krijg ik:
    { “retrieve_reply”:{ “seqnr”:1,”acc_status”:3} }
    Deze response krijg ik ook wanneer ik als het mac_address daadwerkelijk het mac adres van de computer die gepaird is gebruik:
    {“retrieve_message”:{“seqnr”:1,”account_auth”:{“user_account”:”atagone@nerdnieuws.net”,”mac_address”:”52:54:00:A9:72:D7″},”info”:15}}

    Heb jij enig idee wat hier fout gaat? Wellicht ben jij hier ook tegenaan gelopen en kan je me een stuk verder helpen. Ik zou dat zeer op prijs stellen 🙂
    Met vriendelijke groet,
    Gert-Jan Aalderink

    1. Hi Geert-Jan,

      Misschien ben je er inmiddels zelf uitgekomen, maar ik liep tegen hetzelfde aan, met een beetje trial-en-error ben ik erachter gekomen dat je de account_auth {…} mee moet sturen. De API documentatie klopt niet helemaal blijkbaar. 🙁

      {
      “update_message”: {
      “seqnr”: 1,
      “account_auth”: {
      “user_account”: “”,
      “mac_address”: “ab:cd:ef:01:23:45”
      },
      “control”: {
      “ch_mode_temp”: 21.5
      }
      }
      }

  2. Zelf heb ik nooit geleerd te programmeren, anders had ik me er zeker zelf toe gezet maar: is het denkbaar met bovenstaande kennis een plugin voor homebridge te schrijven zodat er homekit (en met name Siri-)ondersteuning voor de Atag One mogelijk is?

Leave a Reply

Your email address will not be published. Required fields are marked *