rcmdnk's blog

Withings Body Cardio フランス生まれのスマート体重計 ブラック Wi-Fi/Bluetooth対応 心臓の健康チェック(心拍/血管年齢)&体組成計 【日本正規代理店品】 WBS04-BLACK-ALL-ASIA

WithingsのAPIの変更があり、2022年2月8日に以前のAPIが使えなくなるということでそれに対応した話。

Withings APIの変更

Withingsの体重計の測定結果やSleepの睡眠測定結果などをGoogle App Script (GAS)を使って Google Sheetsにコピーしています。

これに使っているAPIが変更されるとのこと。

[BREAKING] Shutting down access and refresh tokens endpoints – Withings Support

ちゃんと見てなかったんですが、去年の3月16日にアナウンスされていて、 古いAPIは今年の2月8日で使えなくなる、とのこと。

認証に使うエンドポイントのURLとかが変わるのでまずはそれを変更しなくてはいけませんが、 送るパラメーターや帰ってくるパラメーターも変わるのでちゃんと見てやる必要があります。

主にはGoogleが作っている googleworkspace/apps-script-oauth2 というOAuth2を扱うライブラリを使っているのでそこでの取り扱いの変更です。

Withings-gas

こんな感じのApp Scriptでやっています。

まずはWithings.gs/getService()の中のsetTokenUrlの変更:

1
2
- .setTokenUrl('https://account.withings.com/oauth2/token')
+ .setTokenUrl('https://wbsapi.withings.net/v2/oauth2')

これだけで済めば良いのですが、他がちょっと面倒でした。

新しいAPIではToken取得時にactionというパラメーターが必須になっています。

このToken取得時に必要なパラメーターはgetService()の中で

1
2
+ // Set Token Payload Handler
+ .setTokenPayloadHandler(myHandler)

と、setTokenPayloadHanderを使って関数を使って追加する必要があります。このmyHandler

1
2
3
4
5
6
7
/**
 * TokenPayloadHandler
 */
function myHandler(payload) {
  payload.action = 'requesttoken';
  return payload;
}

最初の認証の際に必要なパラメーターはsetParam('parameter', 'value')みたいな感じでセットできますが、 Token取得時のものはこの様に関数を使って加える必要があります。

これに失敗すると、認証ページに行ってCallback関数が呼ばれた時、

Error: Error retrieving token: Not implemented(行 553、ファイル「Service」)

みたいなエラーが表示されます。これが出た場合はToken取得時にactionの値などが正しくせっとされてません。

うまくいくとapps-script-oauth2のexample通りにCallback関数を作っていればSucess!が出るはず。

これでScriptsに戻ってもう一度実行すればうまく取得できるかと思ったら、今度はスクリプト実行時に

Error: Withings API returns wrong status: 
{"status":401,"body":{},"error":"XRequestID: Not provided invalid_token: The access token provided is invalid"}

これを調べてみると、APIで値を取得時に、

Withings.gs
1
2
3
4
5
6
  var options = {
    headers: {
      Authorization: 'Bearer ' + service.getAccessToken()
    },
    payload: payload
  };

といった感じでヘッダーを定義しているのですが、このservice.getAccessToken()の値がnullになっていました。

この関数は

OAuth2.gs
1
2
3
4
5
6
7
Service_.prototype.getAccessToken = function() {
  if (!this.hasAccess()) {
    throw new Error('Access not granted or expired.');
  }
  var token = this.getToken();
  return token.access_token;
};

https://github.com/googleworkspace/apps-script-oauth2/blob/3a2924dd0adc9eaf92e5fd64c242bd93ffb3f3ff/dist/OAuth2.gs#L557

と定義されていて、どうやら取得したJSONの直下にaccess_tokenがあることが仮定されています。

Withings APIの変更のアナウンス を見てみると、New response formatとあり、the deprecated endpointを使った場合は access_tokenが最上位にいますが、 the new endpointの場合はbodyの下にいます。

なので、上の関数のところでtoken.body.access_tokenの様にしないといけません。

そっちをいじるのは面倒なので、 getToken()で直接取得したJSONデータをとってきてbody.access_tokenを使うようにします。

一応チェックも入れてあります。

Withings.gs
1
2
3
4
5
6
7
8
9
10
11
12
  var access_token = service.getToken().body.access_token;
  if (!access_token) {
    reset();
    throw new Error('Wrong access_token: ' + access_token);
  }

  var options = {
    headers: {
      Authorization: 'Bearer ' + access_token
    },
    payload: payload
  };

これで取れるようになりました。

access_tokenの仕様

googleworkspace/apps-script-oauth2 がどれだけ広く使われているか分かりませんが、 素直に使おうとするとこの様にgetAccessTokenは使えないので 殆どの場合はaccess_tokenが最上位にあるようにAPIの返答が組まれているのだと思います。

なぜ変えたのか。。。 この辺の事情よく知りませんが、 最近はそういう方向なのでしょうか?(status的なものをまずは入れたい?)

Sponsored Links
Sponsored Links

« 人感センサーライトを付けたため壁スイッチのパイロットランプをなんとかする エアコン選び »