Pebble 開発チュートリアル(3-2):PebbleKit JSとの連携

先は長いけれどがんばる!


Preparing AppMessage

watchappでもwatchfaceでも、通信で用いる主なAPIは「AppMessage API」です。

「key」と「value」のセットを用いて、Pebbleとその親機となるスマートフォンとの間で送受信をすることができます。キーとバリューのセットというのは、「key:温度」&「value:29度」とか「key:天気」&「value:晴れ」という感じです。keyを指定して値をセットしたり取得したりできるのです。

1.メッセージやエラーを受け取るための、AppMessageのコールバック関数を作る。
2.コールバック関数をシステムに登録する。
3.AppMessageをオープンする。

オープンすると、メッセージが届くことで AppMessageInboxReceived コールバックが呼び出されるようになりますので、アプリはそれに応じた処理をすればよいと。

コールバック関数は使用される前に定義する必要があるので、init()より上に記載するのが良いでしょう。

static void inbox_received_callback(DictionaryIterator *iterator, void *context) {
}

コールバック関数をあと3つ定義します。メッセージ消失の検出、送信エラーの検出、送信完了の検出です。

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!");
}

それぞれの処理が行われたことがわかるように、APP_LOG関数でログを出力するようにしています。

次に、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 をオープンします。

// Open AppMessage
app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());

メッセージの取りこぼしをしないために、順番として、オープン処理の前に必ず登録処理をしてください。

Pebble側の送受信の処理はこれで完了です。次に、実際に送受信される内容を決めます。

送信時のキーとなる値ですね。整数値で決めます。ここでは
「温度」を「KEY_TEMPERATURE」でプログラムとしての値は「0」
「天気」を「KEY_CONDITIONS」でプログラムとしての値は「1」
とします。

ソースファイルでは#defineで定義しとくといいです。

#define KEY_TEMPERATURE 0
#define KEY_CONDITIONS 1

定義が多い時は、enum使うのもいいっすよ、とのこと。

enum {
  KEY_TEMPERATURE = 0,
  KEY_CONDITIONS = 1
 }

Preparing PebbleKit JS

次は PebbleKit JS の準備です。ここでは、天気情報を PebbleKit JSを通して取得するわけですが、そもそも、PebbleKit JSってナニモンじゃあ、という感じです。そこはまあ、実装しながら、理解していくことにします。

最初に「SOURCE FILES」で「ADD NEW」、FILE TYPEは「JavaScript file」を選択。名前は「weather.js」にして「CREATE」します。

pebble6_06

PebbleKit JS SDKを使うための基本的なテンプレートがありまして、2つのイベントリスナーがあります。
・readyイベント
Pebbleでwatchapp/watchfaceが実行された後で、PebbleKit JSが準備OKになったときに発生。
・appmessageイベント
AppMessageがPebbleから親機スマートフォンに送られたときに発生。

テンプレートは下記のようになります。「weather.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!');
  }
);

ここまでで、いったん動作確認してみます。

pebble6_08

起動完了したら「VIEW LOGS」でログ見てみてください。
うまくいってると「[PHONE] pebble-app.js:?: PebbleKit JS ready!」が出てると思います。
「DISMISS」押しちゃった場合は左メニュー「COMPILATION」の「VIEW APP LOGS」からログ見れます。
Cファイルからは「APP_LOG」、JSファイルからは「console.log()」でログ出力ができるのですね。


「表示準備」「Pebbleとスマートフォンのデータ送受信準備」ができましたので、実際に表示するための情報を取得する部分に入ります。

Getting Weather Information

OpenWeatherMap.org から天気情報を取得してWatchfaceに表示する手順は下記の通り。

1.スマートフォンからユーザの位置情報を取得
2.XMLHttpRequest オブジェクトを使ってOpenWeatherMap API を呼ぶ。このとき1.で取得した位置情報を渡す。
3.XMLHttpRequestの戻り値から取得した情報をPebbleに送り、Watchfaceに表示する。

pebble6_07

最初に「SETTING」の「USES LOCATION」のチェックを入れて、位置情報が使用できるようにします。(「SAVE CHANGES」忘れずに!)

次に位置情報取得の処理をJSに追加します。パラメータとしてtimeoutにタイムアウト時間、maximumAgeにキャッシュ最大保存時間を指定します

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()」が呼ばれます。
うまく位置取得できれば「locationSucess()」が呼ばれて位置情報が入っている「pos」が引数で送られてきます。

次に取得した「pos」を使って天気情報を取得します。
XMLHttpRequest を使って OpenWeatherMap.org から情報取得ですね。

最初にシンプルにリクエストするだけの関数を作りましょう。
locationSuccess();の前に書いておいてください。

var xhrRequest = function (url, type, callback) {
  var xhr = new XMLHttpRequest();
  xhr.onload = function () {
    callback(this.responseText);
  };
  xhr.open(type, url);
  xhr.send();
};

引数は、URL、type(GET または POST)、受信完了時のcallback関数です。
呼び出すURLは、OpenWeatherMapのAPI pageに仕様が載っています。「pos」の値で緯度/経度を指定して、下記のようになります

var url = 'http://api.openweathermap.org/data/2.5/weather?lat=' +
 pos.coords.latitude + '&lon=' + pos.coords.longitude;

typeはGETを使います。戻り値を処理するcallback関数も書いて、全体では下記のようになります。

function locationSuccess(pos) {
  // Construct URL
  var url = 'http://api.openweathermap.org/data/2.5/weather?lat=' +
      pos.coords.latitude + '&lon=' + pos.coords.longitude;

  // 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をパースすることで温度と天気を獲得することができます。全体では、どんな値が返ってきているかというと、
こういう感じです。これはロンドンの例ですね。

戻ってきた値から、下記情報を取得しています。
・main.temp →温度(ケルビンなのでセ氏に直す)
・weather[0].main →天気

その他、風速が知りたければ「wind.speed」を見ればいいですし、いろいろ情報が取れますね。実行してログ確認して、下記のように取得できてればOKです。

[PHONE] pebble-app.js:?: Temperature is 12
[PHONE] pebble-app.js:?: Conditions are Clouds

CloudPebbleでPCで動作させていると、正しい位置は取れないようなのですけども、そこはまあ、しょうがない…。

値が取れるようになったので、あとはこれをPebble側に渡して表示するのじゃあ!