In the previous post we took a look at how the initial just-for-my-own-car implementation was refactored into a version that supported many cars using a database instead of hardcoded values. It also showed that the LogicApp based implementation lost its merrits, and everything was moved into Java code. In this part we’ll take a look at the Tesla API that is used to make the car do things.
The Tesla API is in fact a REST service that is only supposed to be used by the Tesla mobile app, and is not formally supported, nor documented. There are some people who try to maintain some kind of documentation, like teslaapi.io and timdorr.com, by reverse engineering traffic that is sent over the network. So this is all a bit sketchy, but hey, a bit sketchy is ok if it lets you get into a cosy warm car in the morning, without having to wake up early.
The Tesla REST service is running on https://owner-api.teslamotors.com/, and it uses URLs like these to access data:
The URLs include an id when a specific car is addressed, but when listing all vehicles (the first URL) determining what can be shown is done via authentication. And this is where using the API becomes a bit, ahm, volatile.
The API uses two tokens for authentication; an access and refresh token. The access token is send with every request in a HTTP header, but it has a limited lifespan. The refresh token is used to obtain a new set of tokens. These tokens are just long strings of letters and digits, with no intrinsic meaning; not something an end user would remember to type, and the access token changes all the time. That cannot be the starting point.
Getting an initial set of tokens is done by authenticating against the API with a username and password. And this is where the risk lies for the user: handing over that information to a third party will allow that third party to fully access the user’s Tesla account. On the website, via the mobile app, and thus know where the car is, unlock it, by-pass the pin-to-drive, etc. Not a nice idea, but it is what every third party app requires, and thus involves a lot of trust by the user.
The resulting application of this excercise, TeslaTasks, does not ever store the username or password; it only uses them once to obtain an initial set of tokens. These tokens are stored in a database, encrypted using a public / private key. As an alternative, TeslaTask does allow the user to provide a set of tokens to login, but which user is able to generate those manually?
As mentioned, other third party Tesla solutions also require a similar login. As a courtesy to the Tesla community TeslaTasks has a generate tokens page just for generating a token set, which can then be copied to other applications. In this way you only have to trust one third party provider with your username and password. And no, the username and password never get near anything that resembles storage; in fact the whole login process to Tesla is handled on the Tomcat webserver.
Besides people maintaining documentation, there are also language specific implementations of the Tesla API. For Java there are a.o. the TeslaClient by Joe Pasqua, the tesla-client-api by Jonah Hirsch, or the tesla-java-api by Eugene Ryzhikov. I don’t remember exactly why, but I ended up using the one by Joe Pasqua, probably because Google found it first and it had a test class that spit out a lot of info about the car. So it worked.
However, it turned out that the implementation was too heavy. It not only implemented the HTTP communication, but -for example- also parsed the responses in enums, like color codes to color names. But with the amount of changes Tesla makes to its product line up, the Java code is out of date before you have compiled it. Also it introduced additional flags (like ‘valid’) which do not exist in the API. Confusing.
The other implementations were on face value also unnecessary complicated, with services and builders that did not make much sense. So in the end I ended up writing my own TeslaAPI façade, which does just one thing: make the REST API easily callable from Java code. Nothing more, nothing less. Simple is beautiful.
// Start the API final TeslaAPI teslaAPI = new TeslaAPI(); teslaAPI.login(TESLA_USERNAME, TESLA_PASSWORD); // or setTokens // get vehicles List<Vehicle> vehicles = teslaAPI.getVehicles(); Vehicle vehicle = vehicles.get(0); String vehicleId = vehicle.id; // Wake the vehicle up teslaAPI.wakeUp(vehicleId); // Get drive state DriveState driveState = teslaAPI.getDriveState(vehicleId); // Start auto conditioning teslaAPI.startAutoConditioning(vehicleId);
So with that tucked away, it is on to the next problem: Google calendar.