Pebble TimeのWatchFaceにコンフィグ画面を追加する方法

Pebbletimeのアプリとウォッチフェイスには、コンフィグ画面を追加できるようになっています。

f:id:Red-Comet:20150727004717p:plain:w360

(自分のWatchfaceばっかりだ…)
Helvetica Standardの左上に歯車がありますが、この歯車を開くとウォッチフェイスの設定を変えられるようになっています。

ウォッチフェイスを起動してからはボタン操作ができないので、例えば温度を摂氏と華氏に切り替えたりできるものは、ここから変更するわけです。これがあるのとないのでは使い勝手に断然差がつくので前からずっと導入したいと思っていたんですが、このぐらいのレベルになってしまうとサンプルでさえ難解を極めていて、さらに専門用語が多くなるため機械翻訳ではちんぷんかんぷんで、実装まで結構苦労しました。

というわけで備忘録的に、コンフィグ画面を追加するまでの手順をまとめてみようと思います。

コンフィグ画面用のHTMLを用意する

コンフィグ画面は、実は普通のHTMLで作られています。CloudPebbleの中では作れないので、どこかのサーバーにアップロードする必要があります。

とはいえ、サーバーなんて持っていないので困りました。無料で手軽にホームページを作れる方法を検索したのですが、DropboxGoogleドキュメントも今は使えない模様。

しかし捨てる神あれば拾う神ありとはよく言ったもので、GitHubを使えばプロジェクト用の簡易なページを設置できることが分かりました。まあ、これも苦労したんですが…。

GitHubにHTMLを設置する

GitHubのアカウントを作ったらデスクトップ用のアプリをインストールし、新しいプロジェクトを作成します。デスクトップにクローンを作成すると、PC内のフォルダの中身とクラウド上のプロジェクトを同期させることができるようになります。

さらに「gh-pages」というブランチを作り、アプリ側もmainからgh-pagesに切り替えてエクスプローラで開きます。このフォルダに置いたHTMLが公開されることになります。

f:id:Red-Comet:20150727021233p:plain

適当にコミットしてsyncしてから、少し待つと反映されます。

コンフィグ画面用のHTMLを作る

まずはセレクトボックスを設置します。

<form>
<p>
<select id="color" size="4">
	<option value="0" selected>white</option>
	<option value="1">black</option>
	<option value="2">blue</option>
	<option value="3">red</option>
</select>
</p>
<p><input type="submit" id="b-save" value="save"></input></p>
<p><input type="submit" id="b-cancel" value="cancel"></input></p>
</form>

idはセレクトボックスの表札、optionの中のvalueが項目の中身になります。これを元に、以下のスクリプトでPebble側に情報を送ります。

<script>
function saveOptions() {
	var colorValue = parseInt($('#color').val());
	var sendValue =[colorValue];
	return sendValue;
}

$().ready(function() 
	{
	$("#b-cancel").click(function() 
		{
		console.log("Canceling");
		document.location = "pebblejs://close";
	});
	$("#b-save").click(function() {
		console.log("Save");
		var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(saveOptions()));
		document.location = location;
	});
});
</script>

saveボタンを押すとfunction saveOptions()が呼び出されて、セレクトボックスの中身を取り出します。例えばblackを選択している場合は、

var colorValue = parseInt($('#color').val());

で「1」が返ってきます。valueは項目と同じ名前(Blackとか)でもいいんですが、数字のほうが何かと都合がいいと思います。parseIntを使わないと文字列の"1"が戻ってくるので注意。

var sendValue =[colorValue];

このデータを送るわけですが、複数のセレクトボックスがある場合はカンマで区切って配列にできます。

"pebblejs://close#"はコンフィグ画面を閉じるコマンド。これによって次のPebble JS側の"webviewclosed"イベントが発生します。

情報を受け取るためのJavascriptを用意する

前準備ができたので、ようやくCloudpebbleを開きます(^_^;)基本的に天気予報を受け取る仕組みと同じなので、先に練習しておくとここからは結構簡単ですね~。

//ローカルストレージの中身がある場合はローカルストレージを呼び出す
var options = {};
if(localStorage.getItem(1)){options = localStorage.getItem(1); }

Pebble.addEventListener("showConfiguration", function() {
//コンフィグ画面のURL
 Pebble.openURL('http://xxx/index.html?'+encodeURIComponent(JSON.stringify(options)));
});

Pebble.addEventListener("webviewclosed", function(e) {
  options = JSON.parse(decodeURIComponent(e.response));
//ローカルストレージに格納する
 localStorage.setItem(1, options);
//pebbleにキーを送る
  Pebble.sendAppMessage({"COLOR_KEY":options});
});

大事なのは、コンフィグ画面で設定した値をローカルストレージに保存しておくことです。これをやらないとウォッチフェイスを閉じる度に(上下のボタンを押しただけでも)画面が初期状態に戻ってしまうことになります。それと、起動時にローカルストレージの中身をoptionに代入するのですが、初回起動時はif構文で回避しています(中身がある場合しか代入しない)。

HTMLから受け取った情報をoptionsに入れて、Pebble.sendAppMessageで本体に送ります。

とりあえずここまで作ったらビルドしてみて、コンソールに正しい数字が出ているか確認してみるといいですね。

Pebble C側で受け取る処理を加える

では最後の仕上げ、Pebble Cでの処理になります。

まずはキーを宣言。SETTING画面への追加も忘れずに。

enum ColorKey {
  COLOR_KEY = 0x0,         // TUPLE_INT
};

続いてメッセージを受け取るためのapp_message_outboxを作ります。

static void request_color(void) {
  DictionaryIterator *iter;
  app_message_outbox_begin(&iter);

  if (!iter) {
    // Error creating outbound message
    return;
  }

  int value = 1;
  dict_write_int(iter, 1, &value, sizeof(int), true);
  dict_write_end(iter);

  app_message_outbox_send();
}

次は受け取ったデータの処理になります。長くなるので白だけ。あとは似たような感じで続きます。
Aplite対応のため、白と黒以外の色は#ifdefを使って分岐しています。

case COLOR_KEY:
 switch(new_tuple->value->uint8){
  case 0x0:
      background_color = GColorWhite;
      hand_color = GColorBlack;
   #ifdef PBL_COLOR
    sec_color = GColorRed;
   #else
	sec_color = GColorBlack;
   #endif
 break;

こちらも天気予報のサンプルを触ったことがあれば楽勝、と言いたいところなんですが、実は結構つまづきました。

JS側のログでは正しい数字を表示しているのにswitchがdefaultを返してくるというトラブルが発生。試行錯誤した結果、caseを16進数にすることで解決しました。他のキーだと10進数だったりするので原因は謎。まあ、動けばいいや(^_^;)

文字列で"White"とかを返してCStringで受け取って「"GColor" + new_tuple->value->uint8」でs_canvas_colorをGColorWhiteにするという方法も試したのですが、これはダメでした。Textboxに直接流しこむ時以外は、intで取って分岐したほうが良さそうですね。C言語については常にこの「型」の問題で苦労しています(*_*)

続いてwindow_loadに以下を追加します。

Tuplet initial_values[] = {
	TupletInteger(COLOR_KEY, (uint8_t) 0 )
};

app_sync_init(
	&s_sync,
	s_sync_buffer, 
	sizeof(s_sync_buffer),
	initial_values,
	ARRAY_LENGTH(initial_values),
	sync_changed_callback,
	sync_error_callback,
	NULL
);
	
request_color();

initial_valueの設定を忘れたりCStringとuint8_tを取り違えたりすることがままあります。なんで数字で受け取らないんだ!と思った時はここをチェック。

最後にinitにこれを追加して終了です。

app_message_open(255, 255);

ざっと以上のような感じで、コンフィグ画面からウォッチフェイスの色を変えられるようになります。

その他項目を追加したい場合はサンプルを元に改造するといいでしょう。GitHubで検索してみたり、ウォッチフェイスの画面からソースが見れる場合もあるのでオススメ。大抵は高度すぎてチンプンカンプンなのですが…。

今まで作っていたWatchFaceとは違って、HTMLとCSSjavascriptと、さらにCとjavascriptを連携させなければならないところが大変でした。よくこの人達はうまくやっていけてるなあ。通訳になった気分。

プログラム素人が解説記事のようなものを書くのはおこがましいとも思ったのですが、おなじぐらいのレベルの人の手助けになればいいなと思います。正直挫折しそうになったので(-_-;) なんとか流れは把握できたので、これを元にいろいろと作っていこうと思います。

というかこれまで作ったのも改造したいなあ…。摂氏と華氏は最低限表示できないようにしないとあかんか。