コマンドの実行

モバイルアプリからコマンドを送信して Thing を制御することができます。Thing 側でコマンドを処理することで、様々なサービスを実現できます。機能概要は こちら をご覧ください。

コマンドの実行

コマンドを送信するには、コマンドを構成する各アクションにパラメータを設定してから送信メソッドを呼び出します。

実行に先立って、初期化コードの実装 に示す方法によって、初期登録済みの ThingIFAPI インスタンス(api)を入手しておく必要があります。また、実行するユーザーのユーザー ID が必要です。

ここでは スキーマの定義 の例に従ってコマンドの送信例を挙げます。

// Create an array for actions to be executed as a command.
List<Action> actions = new ArrayList<Action>();

// Create actions.
TurnPower action1 = new TurnPower();
action1.power = true;

SetPresetTemperature action2 = new SetPresetTemperature();
action2.presetTemperature = 25;

SetFanSpeed action3 = new SetFanSpeed();
action3.fanSpeed = 5;

// Add the actions to the array.
actions.add(action1);
actions.add(action2);
actions.add(action3);

try {
  // Execute the command.
  Command command = api.postNewCommand("AirConditioner-Demo", 1, actions);
} catch (ThingIFException e) {
  // Handle the error.
}

ここでは以下の処理を行っています。

  1. 送信したいアクションを配列で用意します。

    この例では、3 つのアクションを順番に設定しています。TurnPowerSetPresetTemperatureSetFanSpeed はいずれもスキーマ定義で作成した Action クラスのサブクラスです。定義したコマンドのパラメータを指定しています。

    action1action3 で作成したアクションは、List<Action> に格納しています。Thing 側では、ここで設定した順番どおりにコマンドを受け取ることができます。

  2. コマンドを実行します。

    送信メソッドの引数には 初期化 で使用したスキーマ名、スキーマバージョン、コマンドを指定します。メソッドの戻り値として、Command インスタンスを取得できます。

コマンド実行後の処理の流れは以下のとおりです。

コマンドを実行すると、Thing Interaction Framework がコマンドを受け付けた段階で API は制御を返します。この状態ではまだ Thing にコマンドが配信されておらず、戻り値の Command に含まれるコマンドリザルトは未確定の状態です。Command には実行ごとにコマンド ID が振り出されており、Thing でコマンドが処理されてコマンドリザルトが準備できたときに結果を取得することができます。

Thing Interaction Framework は MQTT プッシュを経由して Thing にコマンド処理のリクエストを出し、指定した 3 つのアクションを Thing 側で処理します。Thing 側では、各アクションに対してハードウェアを制御することで、目的の機能を実行できます。

最終的に、Thing でコマンドを実行した際のエラーコードは Thing Interaction Framework に登録され、そのコマンド ID がプッシュ通知を通じてデバイスに配信されます。プッシュ通知のハンドラーでは、コマンド ID を使って登録されているエラーコードを取得することができます。取得方法は、下記の コマンドリザルトの受信 をご覧ください。

アクションの一部を送信

スキーマで定義されたアクションは、すべてを同時に利用する必要はありません。必要な場面で必要なアクションだけを送信できます。

たとえば、TurnPower.power = false(電源オフ)を送信したい場合、温度や風量の設定は不要であるため、以下のようなコードでコマンドを送信できます。

// Create an array for actions to be executed as a command.
List<Action> actions = new ArrayList<Action>();

// Create an action.
TurnPower action1 = new TurnPower();
action1.power = false;

// Add the action to the array.
actions.add(action1);

try {
  // Execute the command.
  Command command = api.postNewCommand("AirConditioner-Demo", 1, actions);
} catch (ThingIFException e) {
  // Handle the error.
}

コードの詳細は初めの例と同様ですが、配列内のアクションを 1 件だけにしているため、そのアクションだけが送信されます。

詳細情報をつけて送信

コマンドの送信時にタイトルや説明文を登録したい場合、CommandForm を使用します。

// Create an array for actions to be executed as a command.
List<Action> actions = new ArrayList<Action>();

// Create actions.
TurnPower action1 = new TurnPower();
action1.power = true;

SetPresetTemperature action2 = new SetPresetTemperature();
action2.presetTemperature = 25;

SetFanSpeed action3 = new SetFanSpeed();
action3.fanSpeed = 5;

// Add the actions to the array.
actions.add(action1);
actions.add(action2);
actions.add(action3);

// Set metadata to be sent with a command.
JSONObject metadata = new JSONObject("{ \"iconIndex\" : 3, \"issuer\" : \"remoteController\" }");

try {
  // Create a command form from the schema name, schema version, and actions.
  CommandForm form = new CommandForm("AirConditioner-Demo", 1, actions);

  // Set the title, description, and metadata in the command form.
  form.setTitle("Power on");
  form.setDescription("Power on and set to 25 deg C");
  form.setMetadata(metadata);

  // Execute the command with the command form.
  Command command = api.postNewCommand(form);
} catch (ThingIFException e) {
  // Handle the error.
}

実行するコマンドは 1 件目の例と同じですが、CommandForm クラスの setTitlesetDescriptionsetMetadata によって詳細情報を設定しています。これらは コマンドの詳細情報 に示すとおり、アプリケーションの仕様に合わせて自由に利用できます。最大長などの制限値についても コマンドの詳細情報 をご覧ください。

コマンドの実行後、設定した詳細情報は Command クラスの getTitlegetDescriptiongetMetadata メソッドで取得できます。これらのメソッドの実行に必要な Command インスタンスは、登録されているコマンドの一覧取得 に記載の方法でも参照できます。

コマンドリザルトの受信

コマンドが Thing に通知され、Thing でコマンドリザルトが設定されると、FCM によるプッシュ通知を通してモバイルアプリに通知されます。

プッシュ通知のペイロードに含まれるコマンド ID からコマンド全体を取得し、コマンドリザルトを確認することができます。コマンドリザルトには、ステータスコードやエラーメッセージが含まれており、実行結果を知ることができます。

なお、コマンド ID を含むプッシュ通知の送信範囲は、オーナーに設定するユーザーが仮ユーザー(Pseudo User)か通常のユーザーかに応じて決まります。詳しくは、実装のヒント を参照してください。

プッシュ通知の受信ハンドラーのサンプルコードを以下に示します。このコードでは、コマンドリザルトのコマンド ID を取得しています。

public class MyFirebaseMessagingService extends FirebaseMessagingService {
  @Override
  public void onMessageReceived(RemoteMessage remoteMessage) {
    // Get the payload of a push message.
    Map<String, String> payload = remoteMessage.getData();

    // Get the command ID from the payload.
    String returnedID = payload.get("commandID");
  }
}

FirebaseMessagingService のサブクラスによってプッシュ通知を受け取ります。onMessageReceived メソッドでメッセージ受信時の処理を行います。

プッシュメッセージに含まれるペイロードを remoteMessage.getData() によって取得し、その commandID を取得すると、Thing 側で処理されたコマンドの ID を取得できます。なお、プッシュ通知機能を他の用途でも使用している場合は、commandID が null となることがあります。

プッシュ通知が届かないときは

コマンドを処理してもプッシュ通知が届かない場合、Kii Cloud SDK のプッシュ通知機能の トラブルシューティング を参照して問題を解決してください。はじめに、Kii Cloud SDK の チュートリアル にある簡単なプログラムで、Kii Cloud SDK のプッシュ通知機能をテストしてみる方法もあります。

Thing-IF SDK のプッシュ通知機能は、Kii Cloud SDK ものとは、以下の点が異なることをご注意ください。

  • Thing-IF SDK では、常に開発環境と配布環境の両方にプッシュメッセージが通知されます。
  • Thing-IF SDK では、デバイスのインストール処理の実装が異なります。実装方法は こちら に示すとおりです。
  • Thing-IF SDK では、トピックの作成、講読、送信などの Kii Cloud SDK の機能に関連する部分は、初期登録時やイベント発生時に自動的に行われます。

コマンド ID からのコマンドの取得

コマンド ID からコマンドの詳細を取得することができます。これによって、コマンドリザルトの確認処理などを実装できます。

try {
  // Get the command.
  Command command = api.getCommand(returnedID);

  // Get each of the action results.
  List<ActionResult> results = command.getActionResults();
  for (ActionResult result : results) {
    String actionName = result.getActionName();
    boolean succeeded = result.succeeded();
    String errorMessage = result.getErrorMessage();
  }
} catch (ThingIFException e) {
  // Handle the error.
}

ここでは以下の処理を行っています。

  • コマンド ID を引数として、ThingIFAPIapi インスタンス)の getCommand メソッドを呼び出すと、コマンドの全体をサーバーから取得できます。
  • コマンドに格納されている各 ActionResult を取り出して、アクション名、成功のステータス、エラーメッセージを取得できます。なお、アクション名は スキーマ で定義した ActionResult のサブクラスのアクション名として取得できます。

登録されているコマンドの一覧取得

サーバーに登録されているコマンドを全件取得できます。

登録されているコマンドが多数ある場合は、ページネーションを利用します。たとえば、コマンドが 30 件登録されている場合、10 件をページとして、10 件ずつ 3 回に分けて取得することができます。

以下にサンプルコードを示します。

try {
  String paginationKey = null;

  do {
    // Get a list of commands.
    Pair<List<Command>, String> results = api.listCommands(0, paginationKey);
    List<Command> commands = results.first;

    for (Command command : commands) {
      // Do something with each command.
    }

    // Get the next pagination key.
    paginationKey = results.second;
  } while (paginationKey != null);
} catch (ThingIFException e) {
  // Handle the error.
}

listCommands メソッドによってサーバーに登録されたコマンドを一覧取得しています。

paginationKey によって、現在のページの状態を表します。初めに null で listCommands メソッドを呼び出すと、先頭ページが取得でき、さらに、戻り値の second として次のページネーションキーが返されます。これを次の listCommands のページネーションキーとして指定すると、そのページを取得できます。最終的に全件取得されると、ページネーションキーは null を返します。

listCommands の第 1 引数は 1 回に取得するコマンドの件数です。0 以下の数値を指定すると、サーバー側での自動設定になります。ページのサイズは Best Effort のため、値を指定しても、一度に指定件数分を取得できないことがあります。取得できなかったコマンドは、次のページで取得できます。

取得方向は順方向のみで、さかのぼって取得することはできません。

results.first に取得できたコマンドの一覧が入っています。