昨日の日記の続き。今日は Pebble から「Pebble とリンクしたスマートフォン」を通じて Web API から情報取得する部分を作っていくところになる。
チュートリアルの Part3 は PebbleKit JS を使って、Web 上のコンテンツから取得した情報を Pebble アプリ内に表示するチュートリアルになる。
このチュートリアルでは、JavaScript で OpenWeatherMap から現在の天気と気温を取得して表示していく。
まずは、JavaScript で情報を書き換える場所づくりとして、テキストレイヤーを追加していく。これは、時刻表示のときと同じ流れなので分かりやすい。
// Create temperature Layer
s_weather_layer = text_layer_create(GRect(0, 130, 144, 25));
text_layer_set_background_color(s_weather_layer, GColorClear);
text_layer_set_text_color(s_weather_layer, GColorWhite);
text_layer_set_text_alignment(s_weather_layer, GTextAlignmentCenter);
text_layer_set_text(s_weather_layer, "Loading...");
ちょっと違うのは、初期状態で表示するテキストを「Loading...」にしているところかな。JavaScript で情報取得して書き換える前には、こういった表示にしておくほうがユーザにやさしいということだろう。
それから同じフォントでも違うフォントサイズを利用するには、appinfo.json
で改めて定義が必要なようだ。
{
"type": "font",
"name": "FONT_PERFECT_DOS_48",
"file": "fonts/perfect-dos-vga.ttf",
"compatibility": "2.7"
},
{
"type": "font",
"name": "FONT_PERFECT_DOS_20",
"file": "fonts/perfect-dos-vga.ttf",
"compatibility": "2.7"
},
ここでチュートリアルの説明にちょっとワナがある。
チュートリアル Part2 では、フォントファイルの指定を "file": "fonts/perfect-dos-vga.ttf"
のように fonts
ディレクトリ以下にファイルがあるように書いているのに、チュートリアル Part3 では "file":"perfect-dos-vga.ttf"
とディレクトリが存在していない。
上記の JSON は Part2 に合わせてディレクトリが入った状態で書いている。
その他、いつもの unload 処理を追加した後で、build & install すると、このような Loading 表示が出た。
ちなみにフォントサイズの指定は「_48」とか「_20」の部分になるようだ。どういうルールでそうなっているのかは、うまく把握できていないのだけど、「_20」の部分を「_10」に書き換えて build & install してみたら、小さいフォントで表示されたので、今はそういうものだと思っておこう。
Pebble および Pebble と接続しているスマートフォンとの間の通信は、AppMessage API を使って行われるとのこと。
AppMessage の使い方の概要は次のようになるらしい。
この手順で開始されて実際にメッセージが届くと AppMessageInboxReceived
コールバックが呼ばれるそうだ。コールバック関数は、次の4つが紹介されている。カッコ内の関数名はチュートリアルに書かれていた名前であり、これに固定されている訳ではない。
inbox_received_callback()
)inbox_dropped_callback()
)outbox_failed_callback()
)outbox_sent_callback()
)コールバック関数を定義したら、それを init()
の中でシステムへ登録する。
// Register callbacks
app_message_register_inbox_received(inbox_received_callback);
app_message_register_inbox_dropped(inbox_dropped_callback);
app_message_register_outbox_failed(outbox_failed_callback);
app_message_register_outbox_sent(outbox_sent_callback);
AppMessage を ON にするためには「app_message_open()
」を実行してやる必要がある。
// Open AppMessage
app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());
これを実行するのは、コールバック関数を定義した後に行なうのがベストプラクティスだとチュートリアルには書いてある。この順番で実行することで、メッセージの取りこぼしが無くなるみたいだ。
それから、Pebble とスマートフォンの間でデータのやりとりをするためのキーとして、次の定義をしておく。
#define KEY_TEMPERATURE 0
#define KEY_CONDITIONS 1
AppMessage でのメッセージのやりとりのキーは数値のようなのだが、数字でのアクセスだとマジックナンバーになってしまうし、何よりコードがリーダブルでは無くなるので、こうしておくのが普通だそうだ。更に複雑になった場合には enum を利用して整理する方法もあるよと紹介されている。
PebbleKit JS は、src/js/pebble-js-app.js
ファイルに記述していく。どうもこのひとつのファイルに全てを記述していくようだ。
最小のひな形としてサンプルが掲載されている。そのインデントが好みではないので、自分ではこのようにした。最小構成では ready
と appmessage
のイベントを LISTEN するだけのものだ。
/*global Pebble */
Pebble.addEventListener('ready', function(e) {
console.log('PebbleKit JS ready !');
});
Pebble.addEventListener('appmessage', function(e) {
console.log('AppMessage received !');
});
ここまでできたら、また build & install を実行する。そして console.log()
の出力を見てみようというのだ。まず、インストール時に console.log()
の実行結果が表示された。
% pebble install --phone 192.168.1.78
[INFO ] Installation successful
[INFO ] JS: starting app: 0000-0000-0000-0000-0000000000000 Tutorial1
[INFO ] app is ready: 1
[INFO ] JS: Tutorial1: PebbleKit JS ready !
それから、ログを見る方法として pebble logs
コマンドが紹介されている。log
じゃなくて logs
ね。これを実行すると、ログの流し見ができるようになる。
% pebble logs --phone 192.168.1.78
[INFO ] Enabling application logging...
[INFO ] Displaying logs ... Ctrl-C to interrupt.
[INFO ] I ocess_manager.c:297 Heap Usage for App <Tutorial1>: Total Size <22352B> Used <6536B> Still allocated <40B>
[INFO ] JS: stopping app: 0000-0000-0000-0000-0000000000000 Tutorial1
[INFO ] JS: starting app: 0000-0000-0000-0000-0000000000000 Tutorial1
[INFO ] app is ready: 1
[INFO ] JS: Tutorial1: PebbleKit JS ready !
これは既に起動していた Tutorial1 の Watchface を別の Watchface に切り替えてから、もう一度 Tutorial1 へ戻したときの様子。一度 Tutorial1 がストップし別の Watchface が起動(こっちは特にログを吐き出していない)、そして Tutorial1 が再び起動して、console.log()
の内容が表示された。
つまり、Watchface アプリの起動時に pebble-js-app.js
が読み込まれ、イベントリスナーに登録した ready
イベントが発火されたという訳だ。
この logs
サブコマンドでは、PebbleKit JS の console.log()
だけでなく、C 言語での APP_LOG
の内容も出力してくれるそうだ。チュートリアルにも「logs
はデバッグに超便利だよ!」って書かれている。プリントデバッグ万歳!
OpenWeatherMap.org から天気情報を取得して、Watchface アプリに表示させるステップが説明されている。チュートリアルの説明そのままだけど、こんな手順になるそうだ。
この手順は JavaScript の範疇になる。コードを追加していくのは、先ほどと同じ src/js/pebble-js-app.js
だ。
その前に、位置情報を取得するためには appinfo.json
へ capabillities
の定義が必要になるそうなので記述する。
"capabillities": [
"location"
],
JSON 内のどのレベルに書くのか?と思ったのだけど、他のフィールドの配下ではなくトップレベル(という言い方で良いのだろうか?)に書けばいいようだ。
そして改めて JavaScript へ戻る。先ほど作った ready
イベントのリスナーに getWeather()
の呼び出しを追加する。
Pebble.addEventListener('ready', function(e) {
console.log('PebbleKit JS ready !');
getWeather();
});
getWeather()
の中身はこうなっている。
function locationSuccess(pos) {
// We will request the weather here
}
function locationError(err) {
console.log('Error requesting location!');
}
function getWeather() {
navigator.geolocation.getCurrentPosition(
locationSuccess,
locationError,
{timeout: 15000, maximumAge: 60000}
);
}
まず getWeather()
内で navigator.geolocation.getCurrentPosition()
を実行して位置情報を取得する。これは Pebble の独自メソッドではなく、標準の Geolocation API の書式だ。
Pebble がスマートフォンと接続している状態であれば、この API を使ってスマートフォンが持っている位置情報をこの JavaScript で取得できるってことみたいだ。
位置情報が取得できたら、locationSuccess()
が呼ばれ、失敗したら locationError()
が呼ばれる。locationSuccess()
には位置情報のオブジェクトが渡される。
次はこの locationSuccess()
の中身を実装していく。
function locationSuccess(pos) {
var url = 'http://api.openweathermap.org/data/2.5/weather?lat='
+ pos.coords.latitude
+ '&lon='
+ pos.coords.longitude;
xhrRequest(url, 'GET', function(responseText) {
var json = JSON.parse(responseText);
var temperature = Math.round(json.main.temp - 273.15);
console.log('Temperature is ' + temperature);
var conditions = json.weather[0].main;
console.log('Conditions are ' + conditions);
});
}
xhrRequest()
はこのチュートリアルで独自に定義した XMLHttpRequest の処理(この日記には書いてないので、本家チュートリアルを参照すべし)だ。ここでは、getCurrentPosition()
で取得した位置情報を使って OpenWeatherMap.org の API 呼び出し用の URL を作り、それを XMLHttpRequest(つまり Ajax)で呼び出している。レスポンスが返ってきたら、その中身を JSON にパースして、console.log()
へ吐き出している。
ここまで作って build & install したら次のようなログが出てきた。
% pebble install --phone 192.168.1.78
[INFO ] I ocess_manager.c:297 Heap Usage for App <Tutorial1>: Total Size <22352B> Used <6536B> Still allocated <40B>
[INFO ] JS: starting app: 0000-0000-0000-0000-0000000000000 Tutorial1
[INFO ] Installation successful
[INFO ] JS: starting app: 0000-0000-0000-0000-0000000000000 Tutorial1
[INFO ] app is ready: 1
[INFO ] JS: Tutorial1: PebbleKit JS ready !
[INFO ] JS: Tutorial1: Temperature is 18
[INFO ] JS: Tutorial1: Conditions are Rain
気温が 18 度で、天気は雨だ。このとき OpenWeatherMap の Web サイトで東京の天気を見てみたら、18度で雨となっているので、正しく情報が取得できたようだ(これを書いていたときには東京にいた)。
OpenWeatherMap からのレスポンスのサンプルが、このチュートリアルの Gist で公開されている。日の出・日の入りや気圧とかも取得できるみたいだね。
JavaScript での最後のステップと書かれている。このステップで、JavaScript から Pebble アプリへのデータ送信のやり方が分かるはず。
まず、AppMessage で使うキーを appinfo.json
に定義する。
"appKeys": {
"KEY_TEMPERATURE": 0,
"KEY_CONDITIONS": 1
}
これは最初のほうで Tutorial1.c
へ定義した、この部分と一致させるってことだろうな。
#define KEY_TEMPERATURE 0
#define KEY_CONDITIONS 1
次に AppMessage 送信の本体部分を実装していく。その本体はこのような形で Pebble.sendAppMessage()
を使う。temperature
と conditions
には、それぞれ温度と天気の値が入っているものとする。
var dictionary = {
'KEY_TEMPERATURE': temperature,
'KEY_CONDITIONS': conditions
};
Pebble.sendAppMessage(dictionary,
function(e) {
console.log('Weather info sent to Pebble successfully!');
},
function(e) {
console.log('Error sending weather info to Pebble!');
}
);
それから、appmessage
のイベントリスナーにも getWeather()
を仕込んでおけとのこと。これは後から実装する「情報のアップデート」に必要な仕込みらしい。
あともうちょい。
今度は Tutorial1.c
の修正。inbox_received_callback()
の中を実装することになる。AppMessage で送信されたキーごとに文字列を作成し、TextLayer へ出力する流れ。
static void inbox_received_callback(DictionaryIterator *iterator, void *context) {
Tuple *t = dict_read_first(iterator);
while (t != NULL) {
switch (t->key) {
case KEY_TEMERATURE:
snprintf(temperature_buffer, sizeof(temperature_buffer), "%dC", (int)t->value->int32);
break;
case KEY_CONDITIONS:
snprintf(conditions_buffer, sizeof(conditions_buffer), "%s", t->value->cstring);
break;
default:
APP_LOG(APP_LOG_LEVEL_ERROR, "Key %d not recognized!", (int)t->key);
break;
}
t = dict_read_next(iterator);
}
snprintf(weather_layer_buffer, sizeof(weather_layer_buffer), "%s, %s", temperature_buffer, conditions_buffer);
text_layer_set_text(s_weather_layer, weather_layer_buffer);
}
最後に「一定時間で天気情報をアップデートする」という処理を tick_handler()
の中に記述する。tick_handler()
は「1分ごとに時計表示を書き換える」ところで使っていたルーチン。ここに「30分ごとに天気情報を取得して AppMessage を送信する」という処理を追加する。
if (tick_time->tm_min % 30 == 0) {
DictionaryIterator *iter;
app_message_outbox_begin(&iter);
dict_write_uint8(iter, 0, 0);
app_message_outbox_send();
}
ここまで書いて build & install をすると、Watchface に現在位置の天気情報が表示される。さっきログに出てきたように、「18C Rain」と表示された。
これでチュートリアルの全てが終了。なんとなく全体像は把握できたかな。でも JavaScript 部分はともかく、C 言語部分はだいぶ模写しただけ感があるので、もうちょっと知識を得ねばという感じだな。
なにはともあれ、自分で作った画面が Pebble 上に表示されるのは楽しいね、チュートリアルとは言え。