Morning Girl

Web API, Windows, C#, .NET, Dynamics 365/CRM etc..

Tesla API が触りたいけれど、車が高くて買えないので、Tesla API の Mock API Server を作って、擬似的に Tesla を所有している感覚を API で体験する

Tesla ご存知ですか? そう、あの Tesla です。最近日本でも Model 3が出始めて、人気沸騰中(だと個人的に思っている)の電気自動車のことです。

https://www.tesla.com/ja_jp

f:id:sugimomoto:20200120222922p:plain

この Tesla なんですが、iPhoneAndroid 向けのアプリを提供していて、このアプリから車の情報を確認したり、温度調整やロックの解除など、色んな Tesla の操作を行えるようになっています。

https://apps.apple.com/jp/app/tesla/id582007913

f:id:sugimomoto:20200120222932p:plain

【機能一覧】

  • 充電状況をリアルタイムで確認し、充電を開始または停止する
  • 運転前に車両の暖房/冷房を入れる (ガレージ内でも可能)
  • 遠隔からロックまたはロック解除する
  • 車の現在地を確認したり、動きを追跡する
  • お気に入りのアプリから目的地を送信し、ナビを開始します
  • 同乗者はすばやくメディアをコントロールすることができます
  • 駐車場で車のライトを点滅したりホーンを鳴らしたりして車両を見つける
  • パノラマ ルーフを開閉する
  • 車両をガレージまたは狭い駐車スペースから呼び出す(オートパイロット搭載車用
  • どこにいても車両のソフトウェアをアップデートできます
  • パワーウォールへのソーラーからの充電量、家庭でのエネルギー使用量、系統への売電量をモニターする
  • ソーラー発電量とバッテリー消費データをダウンロード

そのアプリの内部通信が Web APIによって行われているんですが、なんとまあその Web APIリバースエンジニアリングして、非公式APIリファレンスとして公開している人が居るのです!(ちなみに2種類存在しますが、こっちのほうが定義が多いっぽい)

https://tesla-api.timdorr.com/

f:id:sugimomoto:20200120222939p:plain

Web API 中毒の人間としては黙っていられず、でもちょっといきなり車を買うのは厳しいなーと思いながら、このリファレンスを「いいなー、いいなー」と冬休み中に眺めていたら、正直かなり充実した機能群が提供されている API なんですよね!(正直このReference だけでご飯三杯はイケる)

ざっとあげただけでも、50種類のエンドポイント(認証1種類、データ取得系が10種類、車両操作系が40種類)(以下のEXCELは私が整理とプライオリティ付けのために作成したエンドポイント一覧)

f:id:sugimomoto:20200121093258p:plain

また、取得できるデータの項目もかなり多岐に渡る! バッテリーの状況やドアの開け締め状態、温度湿度近くのバッテリーチャージポイントの情報まで、「200!」項目近くの車両情報がこのAPIを通じて取得できるようになっています。

https://tesla-api.timdorr.com/vehicle/state/data

f:id:sugimomoto:20200120223051p:plain

ぐうううううううう、これは是が非でも触りたい・・・! 触りたいけれど・・・!

TeslaはWeb上で購入できるようになっていて、一番安価なModel 3の見積もりをしてみたんですが(20回くらい見積もりだけしてます)

f:id:sugimomoto:20200120223116p:plain

だいたい500万円が最低価格で、頭金100万円の月々5万円の60回払いから

オートパイロットとかを含めると、600万近く・・・!

f:id:sugimomoto:20200120223123p:plain

Tesla API 触りたい! けど、ポンと100万も出せるほど余裕があるわけじゃない!どうすればいいんだ!!!

f:id:sugimomoto:20200120223132p:plain

まてよ! とりあえず JSON 返す Mock Server 作れば、触ってる感じが楽しめるんじゃね!? と思い立ち

f:id:sugimomoto:20200120223138p:plain

作ってみました・・・!

できたもの

そして出来上がったのが、以下の Mock API です! とりあえず Azure Web Apps にホスティングしています。OpenAPI 3.0 で書いているので、そのままドキュメントも見れますし、APIを叩けます。

https://orgopenapitools20200118014702.azurewebsites.net/swagger/index.html

f:id:sugimomoto:20200120223145p:plain

実際に以下のようなリクエストを実行すれば、実データをベースとしたダミーデータが取得できます。(ダミーデータは Reference にあったものを使用)

GET https://orgopenapitools20200118014702.azurewebsites.net/api/1/vehicles

f:id:sugimomoto:20200120223151p:plain

これで、バッテリーのチャージ状況から、シートヒーター、近くの充電サイトまで取れる!(ダミーデータだけど!)

アラートでフラッシュライトしたり、ドアのロック・アンロック、最高時速制限の設定から、サンルーフの開け締め、空調システムで温度を上げたり、メディアコントロール、ソフトウェア・アップデートまで、APIでできる!(実際には動かないけど!)

なので

「ほう、現在地はここなのか」

f:id:sugimomoto:20200120225437p:plain

とか

「お、バッテリーレベルが下がっているな。ちょっと近くのチャージャー施設を探そう」

f:id:sugimomoto:20200120225552p:plain

とか

「Sentry モード を ON にして、俺の Tesla(仮想)を守るぜ!」

www.tesla.com

 

 

 

 

 

 

 

 

 

 

 

 

ちょっと寂しい

f:id:sugimomoto:20200120230550p:plain

ちなみに、残念ながら最近実装された Autopark/Summon は API Reference がありませんでした。

tesla-api.timdorr.com

なお Mock API No認証はとりあえずかかってません。なので、皆さん今なら叩き放題です!(実際にTeslaは動かないけどね?

Tesla Mock API の作り方(前置き)

さて、この記事のメインはどうやってこの Tesla Mock API を作ったか? に割きたいと思います。

ちなみに、少しだけ背景をお伝えすると、前述の通り、APIは以下のUnofficial Referenceを参考にしているのですが

https://tesla-api.timdorr.com/

この Reference 、悲しいことに OpenAPI や RAML といった API Spec で書かれていません。

https://github.com/timdorr/tesla-api

1エンドポイントくらいなら、まあ、そのままWeb API を生で書いてもよかったのですが、せっかくなら全エンドポイントしっかり叩けて、最終的に CData Driver や PowerApps / Automate といったツールから簡単に接続できるようにしたい! といった裏目的もあったので、OpenAPI で全エンドポイントを書き直し、そこから必要なServer Mock などを生成するというアプローチを取りました。

https://swagger.io/specification/

なのでざっくりとした手順は以下の通りです。

  • Reference を参考に OpenAPIで API Spec を書き直し
  • 作成した API Spec を元に OpenAPI Generator で Server Mock を生成(ASP.NET Core を使ってます)
  • 生成した Server Mock を Azure Web Apps にデプロイ
  • Option:PowerApps / Automate 向けに OpenAPI(Swagger) 2.0 の API Spec を生成

そもそも Tesla API について

さて、詳しい API の作成前に、まず Tesla API そのものがどんなAPIなのか?についてざっくりとおさえておきます。

認証部分

https://tesla-api.timdorr.com/api-basics/authentication

認証部分は OAuth 2.0 ですが、GrantTypeがPassword Grant で、引数として ClientId・ClientSecret 他、ログイユーザーのID・PWが必要です。

POST /oauth/token?grant_type=password

{
  "grant_type": "password",
  "client_id": "abc",
  "client_secret": "123",
  "email": "elon@teslamotors.com",
  "password": "edisonsux"
}

あとはResponseに含まれるAccessTokenを各エンドポイントの「Authorization : Bearer 」に含めてリクエストすればOKです。リフレッシュする場合は「grant_type=refresh_token」で。

ちなみに、ClientId・ClientSecret はそのまま Tesla Android / iOS アプリで使われているものを利用します。

なお、今回は OpenAPI の認証部分定義は使わず、Tokenエンドポイントだけ通常のPathとして実装しました。

データ取得:GET 系エンドポイント

データ取得系エンドポイントはシンプルです。まず、自分が所持している車両の情報を取得するために「GET /api/1/vehicles」 エンドポイントを実行し、車両IDを特定します。

その後、そのIDをパスに入れて、各種車両の情報 が取得できるようになります。以下のようなリクエストですね。

GET  /api/1/vehicles/{id}/vehicle_data

ちなみに、Dataがすべての情報を返すエンドポイントで、もう少し細かいグループで取得できるのが、Data以外の各種エンドポイントです。単純にすべてのデータを見たいなら、Dataエンドポイントを叩くだけでOKです。

  • Data
  • Charge State
  • Climate State
  • Drive State
  • GUI Settings
  • Vehicle State
  • Vehicle Config
  • Mobile Enabled
  • Nearby Charging Sites

コマンド実行:POST 系エンドポイント

コマンド実行系のPOSTエンドポイントも、データ取得のエンドポイントと同様にTesla の車両IDを特定してからリクエストを行います。

例えば夜の駐車場などで車両の位置を特定するために使う、フラッシュライトのコマンド は以下のような感じです。

POST /api/1/vehicles/{id}/command/flash_lights

なお、コマンド系のレスポンスは車両を起動するためのWakeを除いて、すべて以下のようなレスポンスを返します。

{
  "reason": "",
  "result": true
}

一部パラメータが必要なエンドポイント(例えば Set Valet Mode )も存在します。これはすべてURLパラメータとして指定します。POSTだからといって、FromやJSONで渡すわけではないので、要注意です。

POST /api/1/vehicles/{id}/command/set_valet_mode?on=true&password=1234

Tesla Mock API の作り方(メイン)

とりあえず以上の要素さえおさえておけば、特に難しいAPIというわけではありません。

このReferenceを元にしながら、OpenAPIを愚直に書き起こすだけです。実際に仕上がったものが以下のYAMLです。

https://github.com/sugimomoto/Tesla.API.Mock/blob/master/tesla-api.v1.yaml

ただ、OpenAPI Spec を 生のYAMLで書くのは正直かなりツライです。なのでこの大量のReferenceからOpenAPI Specを書き起こすために、色々とツールを駆使したので紹介していきます。

OpenAPI Spec を書き起こすためのツール:「Stoplishgt Studio」

OpenAPI Specを書くツールはいくつかありますが、私は「Stoplishgt Studio」がオススメです。

https://stoplight.io/

動かしているイメージはこちらから。

「Stoplishgt Studio」を採用するポイントは以下の3つです。

  1. UIベースでリソース・メソッドの一覧性が良い
  2. Modelの定義を参照・使いまわしやすい
  3. 内部組み込みMock ServiceですぐにAPIを試せる

「1.」は上記画面でも分かる通り、リソースとそれらに対応するメソッドの一覧性が良く、必要なエンドポイントとメソッドが網羅されているかどうか確認がしやすいです。

「2.」はレスポンスの定義としてかなりの数のモデルを使い回すことが初めからわかっていたので、かなり重要でした。「Stoplishgt Studio」はSpecの中のモデルだけではなく、プロジェクトベースでモデルを使い回すことができ、一つのSpecを肥大化を防ぎます。

f:id:sugimomoto:20200120223838p:plain

「3.」は以下のようにパスを追加した段階でMock Serviceが自動で生成されるというものです。

f:id:sugimomoto:20200120223844p:plain

リンクをクリックすれば、そのままGETリクエストがブラウザで実行されて、即エンドポイントの仕様を確認できます。もちろんPostmanから実行してもOKです。

f:id:sugimomoto:20200120223849p:plain

Model の作成:「Swagger toolbox」

先に述べたように取得項目のJsonプロパティは200項目くらい最低でもあって、Model 作成が結構大変です。

認証のレスポンスだけでも、気が遠くなります。

type: object
properties: 
  grant_type: 
    type: "string"
    example: "password"
  client_id: 
    type: "string"
    example: "abc"
  client_secret: 
    type: "string"
    example: "123"
  email: 
    type: "string"
    example: "elon@teslamotors.com"
  password: 
    type: "string"
    example: "edisonsux"

しかし「Swagger toolbox」というJOSNからOpenAPI Spec のモデルを自動生成してくれるツールで一気に自動生成してしまいました。

https://swagger-toolbox.firebaseapp.com/

f:id:sugimomoto:20200120223856p:plain

https://kageura.hatenadiary.jp/entry/swaggertoolbox

なので、以下のようなJSONデータも上記のYAMLにサクッと変換。

{
  "grant_type": "password",
  "client_id": "abc",
  "client_secret": "123",
  "email": "elon@teslamotors.com",
  "password": "edisonsux"
}

変換したYAMLは Stoplishgt のプロジェクトに取り込んで、すべて定義してしまいました。

f:id:sugimomoto:20200120223905p:plain

これで、あとはひたすらPathを追加して、Modelを紐付けていくという単純作業になりました。下調べの時間を省くと、実質1時間ほどで完成したかなと思います。

Server プログラムの生成

これで OpenAPI Specが出来上がったら、あとは Mock Serverを生成してデプロイするだけです。

これは OpenAPI のエコシステムに則って、OpenAPI Generatorを素直に使いました。生成したプログラムは ASP.NET Core ベースです。

https://openapi-generator.tech/

手順は簡単。 npm install で OpenAPI Generator を追加します。npm なのであらかじめ、Node.jsを入れておきましょう。

> npm install @openapitools/openapi-generator-cli -g

あとは、以下のようなコマンドで生成できます。

> openapi-generator generate -i XXXX.yaml -o ./asp-net -g aspnetcore

特に面倒なことはしていないですが、Generate Option はこちら。

https://openapi-generator.tech/docs/generators/aspnetcore

こんな感じで生成されます。

f:id:sugimomoto:20200120223913p:plain

素晴らしいのは、Model定義でちゃんとExampleを入力しておけば、それに従って、ダミデータを返してくれるエンドポイントを実装してくれるところです。

        [HttpPost]
        [Route("/oauth/token")]
        [ValidateModelState]
        [SwaggerOperation("PostOauthTokengrantTypepassword")]
        [SwaggerResponse(statusCode: 200, type: typeof(AuthenticationResponse), description: "OK")]
        public virtual IActionResult PostOauthTokengrantTypepassword([FromHeader]string grantType, [FromBody]AuthenticationRequest authenticationRequest)
        { 

            //TODO: Uncomment the next line to return response 200 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
            // return StatusCode(200, default(AuthenticationResponse));
            string exampleJson = null;
            exampleJson = "{\r\n  \"access_token\" : \"abc123\",\r\n  \"refresh_token\" : \"cba321\",\r\n  \"created_at\" : 1538359034,\r\n  \"token_type\" : \"bearer\",\r\n  \"expires_in\" : 3888000\r\n}";
            
            var example = exampleJson != null
            ? JsonConvert.DeserializeObject<AuthenticationResponse>(exampleJson)
            : default(AuthenticationResponse);
            //TODO: Change the data returned
            return new ObjectResult(example);
        }

あとは、このプログラムをそのまま Azure Web Appsにデプロイするだけで今回の Mock API は完成となりました!

おわりに

次回は実際にこのAPIをツールやサービスから使ってみます!(Teslaは動かないけどね!

2020/01/21 23時、以下の記事を追加しました。

kageura.hatenadiary.jp