前回のチュートリアルパートでは、時刻を表示するシンプルなウォッチフェイスを作成し、カスタムフォントと背景ビットマップで改善しました。これらの要素でできることはたくさんあります。たとえば、ビットマップを追加したり、日付を表示する TextLayer を追加したりできますが、さらに高い目標を目指しましょう。このパートは前回よりも長いので、始める前にお気に入りの温かい飲み物を用意しておいてください!
このチュートリアルでは、PebbleKit JSを使用して Web から取得した追加コンテンツをウォッチフェイスに追加します。SDK のこの部分では、JavaScript を使用して Web にアクセスしたり、スマートフォンの位置情報サービスやストレージにアクセスしたりできます。さらに、設定画面を表示して、ユーザーがウォッチフェイスやアプリの外観や動作をカスタマイズできるようにすることもできます。
このチュートリアルの最後には、以下のようなカスタマイズされたウォッチフェイスが完成します:



前回のパートから続けるには、既存の Pebble プロジェクトを変更するか、新しいプロジェクトを作成して、そのプロジェクトのメインの .c ファイルのコードを開始テンプレートとして使用してください。参考までに、そのコードは
このようになっているはずです。
取得するコンテンツは、OpenWeatherMapからの現在の気象条件と気温です。この追加コンテンツを表示するために、新しい TextLayer が必要です。それでは、以前と同じように、C ファイルの先頭で行いましょう:
static TextLayer *s_weather_layer;
いつものように、既存の要素の後に main_window_load() で適切に作成します。以下は TextLayer の設定です。前の 2 つのチュートリアルパートから、すべて馴染みがあるはずです:
// Create temperature Layer
s_weather_layer = text_layer_create(
GRect(0, PBL_IF_ROUND_ELSE(125, 120), bounds.size.w, 25));
// Style the text
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...");
時刻表示と同じフォントを使用しますが、フォントサイズを小さくします。
package.json に別のフォントを追加するには、最初のフォントのエントリを media 配列内で複製し、name フィールドに示されているフォントサイズを _20 などに変更します。以下は両方のフォントを示す例です:
"media": [
{
"type":"font",
"name":"FONT_PERFECT_DOS_48",
"file":"perfect-dos-vga.ttf",
"compatibility": "2.7"
},
{
"type":"font",
"name":"FONT_PERFECT_DOS_20",
"file":"perfect-dos-vga.ttf",
"compatibility": "2.7"
},
]
それでは、前回と同じようにそのフォントを読み込んで適用します。まず、ファイルの先頭で新しい GFont を宣言します:
static GFont s_weather_font;
次に、リソースを読み込んで新しい TextLayer に適用し、それをメインの Window の子レイヤーとして追加します:
// Create second custom font, apply it and add to Window
s_weather_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_PERFECT_DOS_20));
text_layer_set_font(s_weather_layer, s_weather_font);
layer_add_child(window_get_root_layer(window), text_layer_get_layer(s_weather_layer));
最後に、いつものように、main_window_unload() に他のすべてと同じ破棄呼び出しを追加します:
// Destroy weather elements
text_layer_destroy(s_weather_layer);
fonts_unload_custom_font(s_weather_font);
コンパイルしてインストールすると、ウォッチフェイスは次のようになります:



すべての Pebble ウォッチアプリとウォッチフェイスの主要な通信方法は AppMessage API です。これにより、ウォッチと接続されたスマートフォン間の送信用のキーバリューディクショナリを構築できます。この通信を有効にするために従う標準的な手順は次のとおりです:
AppMessage コールバック関数を作成する。AppMessage を開いてアプリの通信を許可する。このプロセスが実行されると、受信メッセージは AppMessageInboxReceived コールバックへの呼び出しを引き起こし、その内容に反応できるようになります。さあ、始めましょう!
コールバックは、コードファイル内で参照される前に配置する必要があるので、適切な場所は、それらを登録する init() の上です。AppMessageInboxReceived の関数シグネチャを以下に示します:
static void inbox_received_callback(DictionaryIterator *iterator, void *context) {
}
また、ドロップされたメッセージなど、発生する可能性のあるすべての結果とエラーを確認できるように、他の 3 つのコールバックを作成して登録します。これらは、現時点では APP_LOG への呼び出しで報告されますが、
より詳細な情報を取得できます:
static void inbox_dropped_callback(AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_ERROR, "Message dropped!");
}
static void outbox_failed_callback(DictionaryIterator *iterator, AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_ERROR, "Outbox send failed!");
}
static void outbox_sent_callback(DictionaryIterator *iterator, void *context) {
APP_LOG(APP_LOG_LEVEL_INFO, "Outbox send success!");
}
これが配置されたら、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);
そして最後の 3 番目のステップは、app_message_register_inbox_received() の直下で、ウォッチフェイスが受信メッセージを受信できるように AppMessage を開くことです。メッセージが見逃されないようにするために、AppMessage を開く前にコールバックを登録することがベストプラクティスと考えられています。以下のコードスニペットは、inbox と outbox のサイズ(バイト単位)を指定する 2 つの変数を使用してこのプロセスを示しています:
// Open AppMessage
const int inbox_size = 128;
const int outbox_size = 128;
app_message_open(inbox_size, outbox_size);
アプリに適切なバッファサイズを使用する方法については、 Buffer Sizes をお読みください。
気象データ自体は、ウォッチフェイスの JavaScript コンポーネントによってダウンロードされ、ウォッチフェイスが開かれるたびに接続されたスマートフォン上で実行されます。
PebbleKit JS の使用を開始するには、プロジェクトに src/pkjs/index.js という新しいファイルを追加して、JavaScript コードを含めます。
すぐに開始できるように、PebbleKit JS SDK を使用するための基本的なテンプレートを提供します。このテンプレートには、2 つの基本的なイベントリスナーがあります。1 つは 'ready' イベント用で、起動後にスマートフォン上の JS 環境が最初に利用可能になったときに発火します。2 つ目は 'appmessage' イベント用で、ウォッチからスマートフォンに AppMessage が送信されたときに発火します。
以下のテンプレートを JS ファイルの開始に使用してください:
// Listen for when the watchface is opened
Pebble.addEventListener("ready", function (e) {
console.log("PebbleKit JS ready!");
});
// Listen for when an AppMessage is received
Pebble.addEventListener("appmessage", function (e) {
console.log("AppMessage received!");
});
ウォッチフェイスをコンパイルしてインストールした後、アプリログを開きます。
pebble logs を実行してアプリログを監視できます。スマートフォンの IP アドレスを --phone スイッチで指定します。例えば:
pebble logs --phone 192.168.1.78
これらの 2 つのコマンドを 1 つにまとめることもできます:
pebble install --logs --phone 192.168.1.78
上記のスニペットで JS コンソールに console.log() を使用して表示するように設定されたメッセージと一致するメッセージが表示されるはずです!これは、C ファイルの APP_LOG または JS ファイルの console.log() を使用して送信された情報が表示される場所で、デバッグに非常に役立ちます!
OpenWeatherMap.orgから気象情報をダウンロードするために、JS ファイルで 3 つのステップを実行します:
XMLHttpRequest オブジェクトを使用して OpenWeatherMap API を呼び出す。package.json ファイルの capabilities 配列に location を追加する必要があります。これにより、ウォッチアプリがスマートフォンの位置情報サービスにアクセスできるようになります。以下のコードセグメントに示されています:
"capabilities": ["location"]
次のステップは簡単に実行でき、以下コードにすべて内容が示されています。使用している方法では、ユーザーの位置情報を要求した後で、成功と失敗の条件のコールバックとして使用する他の 2 つの関数が必要です。また、リクエストの timeout とデータの maximumAge という他の 2 つの情報も必要です:
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,
});
}
// Listen for when the watchface is opened
Pebble.addEventListener("ready", function (e) {
console.log("PebbleKit JS ready!");
// Get the initial weather
getWeather();
});
ready イベントが発生すると、getWeather() が呼び出され、次に getCurrentPosition() が呼び出されることに注意してください。これが成功すると、locationSuccess() が呼び出され、単一の引数 pos が提供されます。これには、気象情報リクエストを行うために必要な位置情報が含まれています。それでは、それを行いましょう。
次のステップは、OpenWeatherMap.org へのリクエストを行うために XMLHttpRequest オブジェクトを組み立てて送信することです。これを簡単にするために、その使用を簡素化する関数を提供します。これを locationSuccess() の前に配置してください:
var xhrRequest = function (url, type, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
callback(this.responseText);
};
xhr.open(type, url);
xhr.send();
};
xhrRequest() を呼び出すときに提供する必要がある 3 つの引数は、URL、リクエストのタイプ(例:GET または POST)、およびレスポンスが受信されたときのコールバックです。URL は OpenWeatherMap API ページで指定されており、getCurrentPosition() によって提供される座標(最後にエンコードされた緯度と経度)が含まれています:
2015年10月現在、OpenWeatherMapのデータを取得するにはAPIキーが必要です。 これらはOpenWeatherMap.orgから無料で取得できます。
var url =
"http://api.openweathermap.org/data/2.5/weather?lat=" +
pos.coords.latitude +
"&lon=" +
pos.coords.longitude +
"&appid=" +
myAPIKey;
XHR のタイプは 'GET' リクエストで、サービスから情報を取得します。可読性のためにコールバックを関数呼び出しに組み込みます。完全なコードスニペットを以下に示します:
function locationSuccess(pos) {
// Construct URL
var url =
"http://api.openweathermap.org/data/2.5/weather?lat=" +
pos.coords.latitude +
"&lon=" +
pos.coords.longitude +
"&appid=" +
myAPIKey;
// Send request to OpenWeatherMap
xhrRequest(url, "GET", function (responseText) {
// responseText contains a JSON object with weather info
var json = JSON.parse(responseText);
// Temperature in Kelvin requires adjustment
var temperature = Math.round(json.main.temp - 273.15);
console.log("Temperature is " + temperature);
// Conditions
var conditions = json.weather[0].main;
console.log("Conditions are " + conditions);
});
}
したがって、位置情報が正常に取得されると、xhrRequest() が呼び出されます。レスポンスが到着すると、JSON オブジェクトが解析され、気温と気象条件が取得されます。JSON オブジェクトの構造を発見するには、console.log(responseText) を使用してその内容を確認できます。
json.weather[0].main などの上記のステートメントにどのように到達したかを確認するには、ロンドン、英国の
レスポンス例
があります。json と呼ばれる変数(構造のルートを表す)から JSON 構造をたどることで、任意のデータアイテムにアクセスできることがわかります。したがって、風速を取得するには json.wind.speed にアクセスする、といった具合です。
最後の JS ステップは、気象データをウォッチに送り返すことです。これを行うには、送り返す appmessage キーを選択する必要があります。気温と現在の条件を表示したいので、それぞれに 1 つのキーを作成します。
以下の例のキーに示されているように、package.json の messageKeys オブジェクトに AppMessage キーを追加できます:
"messageKeys": [
"TEMPERATURE",
"CONDITIONS",
]
データを送信するには、気象情報の変数 temperature と conditions をディクショナリに組み立てた後、Pebble.sendAppMessage() を呼び出します。オプションで、成功と失敗のコールバックとして 2 つの関数を指定することもできます:
// Assemble dictionary using our keys
var dictionary = {
TEMPERATURE: temperature,
CONDITIONS: conditions,
};
// Send to Pebble
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() への別の呼び出しを追加しましょう。これを実現するために、ウォッチから AppMessage を送信します:
// Listen for when an AppMessage is received
Pebble.addEventListener("appmessage", function (e) {
console.log("AppMessage received!");
getWeather();
});
Pebble 側の最後のステップは、PebbleKit JS から受信した情報に対して行動し、まさにこの目的のために作成した TextLayer に気象データを表示することです。これを行うには、C コードファイルに戻り、AppMessageInboxReceived の実装(前述の inbox_received_callback() など)を見つけます。これは、受信したデータを処理するように変更されます。ウォッチがウォッチフェイスの JS 部分から AppMessage メッセージを受信すると、このコールバックが呼び出され、コールバックシグネチャに見られるように、DictionaryIterator オブジェクトの形式でデータのディクショナリが提供されます。MESSAGE_KEY_TEMPERATURE と MESSAGE_KEY_CONDITIONS は、package.json で指定したとおりに自動的に提供されます。
ディクショナリを調べる前に、3 つの文字バッファを追加します。1 つは気温用、1 つは条件用、もう 1 つは文字列全体を組み立てるためのものです。オーバーランを防ぐために、バッファサイズは余裕を持たせてください:
// Store incoming information
static char temperature_buffer[8];
static char conditions_buffer[32];
static char weather_layer_buffer[32];
次に、snprintf() を使用して適切な Tuple を 2 つのバッファに読み込むことで、受信情報を保存します:
// Read tuples for data
Tuple *temp_tuple = dict_find(iterator, MESSAGE_KEY_TEMPERATURE);
Tuple *conditions_tuple = dict_find(iterator, MESSAGE_KEY_CONDITIONS);
// If all data is available, use it
if(temp_tuple && conditions_tuple) {
snprintf(temperature_buffer, sizeof(temperature_buffer), "%dC", (int)temp_tuple->value->int32);
snprintf(conditions_buffer, sizeof(conditions_buffer), "%s", conditions_tuple->value->cstring);
}
最後に、この if ステートメント内で、完全な文字列を組み立てて TextLayer に表示するように指示します:
// Assemble full string and display
snprintf(weather_layer_buffer, sizeof(weather_layer_buffer), "%s, %s", temperature_buffer, conditions_buffer);
text_layer_set_text(s_weather_layer, weather_layer_buffer);
再コンパイルして再インストールすると、以下に示すものと同様のウォッチフェイスが表示されます:



テキストが画面に対して大きすぎる場合は、media 配列内のそのリソースのエントリの package.json でフォントサイズを小さくできることを忘れないでください。.c ファイル内の定数を新しいリソースの name に一致するように変更することを忘れないでください。
実行する追加のステップは、ウォッチフェイスが読み込まれるときに加えて、定期的な気象更新を取得するように C コードを変更することです。これを行うには、すでにあるタイマーソース、つまり tick_handler() と呼んだ TickHandler 実装を利用します。メインの .c ファイルの tick_handler() の最後に次のコードを追加して、30 分ごとに気象更新を取得するように変更しましょう:
// Get weather update every 30 minutes
if(tick_time->tm_min % 30 == 0) {
// Begin dictionary
DictionaryIterator *iter;
app_message_outbox_begin(&iter);
// Add a key-value pair
dict_write_uint8(iter, 0, 0);
// Send the message!
app_message_outbox_send();
}
前に appmessage JS イベントハンドラに getWeather() への呼び出しを追加したおかげで、TickHandler でのこのメッセージ送信により、新しい気象データがダウンロードされてウォッチに送信されます。完了です!
長いチュートリアルでした!学んだことは以下のとおりです:
AppMessage の準備と開始。navigator.getCurrentPosition() によるユーザーの現在位置の取得。AppMessage の送受信。これらすべてを使用して、膨大な数の Web サービスに GET および POST してデータを表示し、これらのサービスを制御することができます。
いつものように、以下のボタンを使用して、提供されているサンプルコードとコードを比較できます。
チュートリアルの次のセクションでは、Battery Service を紹介し、ウォッチフェイスにバッテリーバーを追加する方法を説明します。