FitBit Versa 用ウォッチフェイスの開発

Posted 2 months ago by yoosee.
  programming versa fitbit

FitBit Versa 用ウォッチフェイス Obsidian を書いた際に使った FitBit Dev の開発概要を以下にざっくり記載する。基本的には dev.fitbit.com がそれなりに充実しているので (痒い所に手が届かない感はあるのだが) そちらを参考すればよい。

FitBit Versa 向けの開発環境

FitBit Versa (とIonic) 向けの開発環境は dev.fitbit.com にSDKやReferenceなどの開発情報がまとまっていて、 Pebble のようにオンラインの開発環境 FitBit Studio が用意されているので開発自体は簡単に始められる。Cloud Pebble と比べると、Github などへの連携がないことと、シミュレーターが外付け(Windows用アプリがある)なこと等が違いで、特に Github 連携は早く対応して欲しい。なお開発は全て FitBit アカウントに紐ついて利用できる。

FitBit Studio

開発自体は Pebble と比べるとだいぶ今どきで、基本は全て JavaScript で、UIはSVG(XML)で記載する。UI の操作は基本的にSVGに対するJavaScriptによるDOM操作で行い、画像や文字、Shape などオブジェクトの操作を行う。色やフォントなどはCSSで設定できることもあり、感覚的にはほぼHTML UIのプログラミングだ。JavaScript も最近の版、少なくともES6相当で、const, let や fetch() なども普通に使える。

実機デバッグ

開発は基本 Simulator をインストールすれば実施できるが、timing issue などで実機に入れると動かなくなったりすることがあったので一応実機確認した方がよいかも。実機試験はスマホ側で FitBit App から Developer Menu の Developer Bridge を有効にするのと、Versa の設定から同じく Developer Bridge を有効にしてから FitBit Studio で接続先を選択すればよい。この際、スマホだけでなく Versa も Wifi 接続している必要があるようなので要注意。また FitBit Gallery App Manager に .fba ファイルをアップロードすると作成されるアプリのリンクを使ってインストールすることもできる。この直接リンクは FitBit が Review for Publish を Approve する前から利用できる。

フォルダとファイル構成

基本的にどのファイルがどういう用途で呼ばれるかのディレクトリ構成が決まっていて、概ね以下のようになっている。これ以外のファイルのおき場所は任意で、相対パスで呼び出せばよろしい。

  • app/index.js : アプリ起動時に呼ばれる main ファイル。他の実行時ファイルは基本的にここから呼び出す
  • companion/index.js : スマホ側で実行されるコンパニオンのファイル。インターネットアクセスや設定呼び出し時の処理など
  • resources/ : GUI関係のファイル用フォルダ。画像ファイルなどのリソースも一般的にはこの下
    • resources/index.gui : GUI設定のSVG定義
    • resources/widgets.gui : GUI設定時に外部からの import や、<use> で再利用する定義を記載
    • resources/styles.css : SVGの id や class 定義用CSSファイル。x, y などの座標もCSS側で設定可能
  • settings/index.jsx : 設定画面の定義ファイル。React っぽく書く。Companion 側での処理記載が必要
  • packages.json : 謎の拡張子JSON。Permission や UUID などを記載する。FitBit Studio であればUIで設定可

例えばSVGファイルの resources/index.gui で秒針を定義して

<g id="secs" pointer-events="visible" transform="translate(50%,50%)">
   <rect x="$-2" y="-120" width="2" height="120" class="secondhand" />
</g>

動作は app/index.js (及びそこから呼ばれるファイル)で定義して ”tick” イベントからの Callback で秒針を動かす。

let secHand = document.getElementById("secs");
...
const clockCallback = (data) => {
...
  secHand.groupTransform.rotate.angle = data.secondsAngle;
};

アイコンなどの画像素材は Fitbit/sdk-design-assets から利用可能になっている。イメージファイルは 8-bit grayscale png で作るとsvgでの色指定が適用されるので、アイコンや時計の数字などはそうしたフォーマットで作るといいっぽい。

固有の部分としては Versa 本体とスマートフォン側の Companion に処理分割があり、アプリの設定やインターネットへの接続が必要な場合などは Companion 側に処理を message を使って投げ込み、また message で受け取ってやり取りする。message 処理を含め非同期処理が結構多いので、予め用意された Event 処理や、EventListener に callback を登録していく形が多い。

  clock.addEventListener("tick", tickHandler);
messaging.peerSocket.addEventListener("message", function(evt) {   
  console.log("Weather in weather/main onmessage: " + JSON.stringify(evt.data));
  weather.update(evt.data);
  onweatherupdated(weather);
})

とかこんな感じ。

実装のサンプルコード

実際のこの辺りは FitBit がサンプルコードを公開しているのでそれを参考にするのが手っ取り早い。ヘルス情報付きの時計は Fitbit/sdk-moment に大体の機能があるのでこれを基本にいじっている。天気の取得などは Pebble のコードを流用しつつ書いているが、OpenWeather を使った実装もサンプルがあるのでさほど難しさはない。

などと書いているが実際には本来 Callback で書くべきを平たく書いたせいで呼び出しのタイミングがおかしくなって動かなかったり、onmessage() のような Singleton を複数定義してしまって定義が上書きされて挙動がおかしくなったり、 Simulator では動いたのに実機テストで動かなくて四苦八苦したりと、いろいろとハマりポイントはあったのだった。実機の接続がまた不安定だったり side loading が失敗したりとか問題だらけでこれが…

とは言え複雑なことをしなければ時計を書くこと自体は大変簡単なので、興味ある人はトライしてみるとよいかと。自分のデザインした時計が自分の手首で動いているのは結構楽しくて、画面を表示させてはニヤニヤしてしまうよ。