コマンドの実行

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

コマンドの実行

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

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

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

  • // Create an action array.
    const actions = [
      {"turnPower":{"power":true}},
      {"setPresetTemperature":{"presetTemperature":25}},
      {"setFanSpeed":{"fanSpeed":5}}
    ];
    
    // Create a command request.
    const commandRequest = new ThingIF.PostCommandRequest("AirConditioner-Demo", 1, actions);
    
    // Execute the command.
    api.postNewCommand(commandRequest).then((command: ThingIF.Command) => {
      // Do something.
    }).catch((error: ThingIF.ThingIFError) => {
      // Handle the error.
    });
  • // Create an action array.
    var actions = [
      {"turnPower":{"power":true}},
      {"setPresetTemperature":{"presetTemperature":25}},
      {"setFanSpeed":{"fanSpeed":5}}
    ];
    
    // Create a command request.
    var commandRequest = new ThingIF.PostCommandRequest("AirConditioner-Demo", 1, actions);
    
    // Execute the command.
    api.postNewCommand(commandRequest).then(
      function(command) {
        // Do something.
      }
    ).catch(
      function(error) {
        // Handle the error.
      }
    );

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

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

    この例では、スキーマで設計した turnPowersetPresetTemperaturesetFanSpeed の 3 つのアクションを actions に設定しています。

    actions はアクションの配列です。各アクションは、アクション名とそのパラメータを表すオブジェクトで表現され、キーと値は 1 組だけを格納します。パラメーターには任意のキーと値の組を指定できます。以下に構成を示します。

    アクションのパラメータの形式は任意です。JSON で表現できれば、値の形式は問いません。

    Thing 側では、actions に設定した順番どおりにコマンドを受け取ることができます。

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

    postNewCommand メソッドを実行してコマンドを実行します。メソッドの引数にはスキーマ名、スキーマバージョン、アクション配列を指定します。スキーマ名とスキーマバージョンは、Thing 側で JSON を解析する際に想定されるパラメータ形式を識別するために使用できます。詳細は 機能ガイド をご覧ください。

    実行は非同期で行われ、完了すると Promise によって Command とエラー情報が通知されます。

Promise で通知された後の処理の流れは以下のとおりです。

コマンドを Thing Interaction Framework が受け付けると、Promise の then が呼び出されます。この状態ではまだ Thing にコマンドが配信されておらず、パラメータの Command に含まれるコマンドリザルトは未確定の状態です。Command には実行ごとにコマンド ID が振り出されており、Thing でコマンドが処理されてコマンドリザルトが準備できたときに結果を取得することができます。

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

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

アクションの一部を送信

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

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

  • // Create an action array.
    const actions = [
      {"turnPower":{"power":false}}
    ];
    
    // Create a command request.
    const commandRequest = new ThingIF.PostCommandRequest("AirConditioner-Demo", 1, actions);
    
    // Execute the command.
    api.postNewCommand(commandRequest).then((command: ThingIF.Command) => {
      // Do something.
    }).catch((error: ThingIF.ThingIFError) => {
      // Handle the error.
    });
  • // Create an action array.
    var actions = [
      {"turnPower":{"power":false}}
    ];
    
    // Create a command request.
    var commandRequest = new ThingIF.PostCommandRequest("AirConditioner-Demo", 1, actions);
    
    // Execute the command.
    api.postNewCommand(commandRequest).then(
      function(command) {
        // Do something.
      }
    ).catch(
      function(error) {
        // Handle the error.
      }
    );

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

詳細情報をつけて送信

コマンドの送信時にタイトルや説明文を登録することもできます。

  • // Create an action array.
    const actions = [
      {"turnPower":{"power":true}},
      {"setPresetTemperature":{"presetTemperature":25}},
      {"setFanSpeed":{"fanSpeed":5}}
    ];
    
    // Set a title, description, and metadata to be sent with a command.
    const title = "Power on";
    const description = "Power on and set to 25 deg C";
    const metaData = {"iconIndex":3, "issuer":"remoteController"};
    
    // Create a command request from the detailed information.
    const commandRequest = new ThingIF.PostCommandRequest("AirConditioner-Demo", 1, actions, null, title, description, metaData);
    
    // Execute the command.
    api.postNewCommand(commandRequest).then((command: ThingIF.Command) => {
      // Do something.
    }).catch((error: ThingIF.ThingIFError) => {
      // Handle the error.
    });
  • // Create an action array.
    var actions = [
      {"turnPower":{"power":true}},
      {"setPresetTemperature":{"presetTemperature":25}},
      {"setFanSpeed":{"fanSpeed":5}}
    ];
    
    // Set a title, description, and metadata to be sent with a command.
    var title = "Power on";
    var description = "Power on and set to 25 deg C";
    var metaData = {"iconIndex":3, "issuer":"remoteController"};
    
    // Create a command request from the detailed information.
    var commandRequest = new ThingIF.PostCommandRequest("AirConditioner-Demo", 1, actions, null, title, description, metaData);
    
    // Execute the command.
    api.postNewCommand(commandRequest).then(
      function(command) {
        // Do something.
      }
    ).catch(
      function(error) {
        // Handle the error.
      }
    );

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

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

コマンドリザルトの受信

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

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

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

プッシュ通知の受信ハンドラーのサンプルコードを以下に示します。

  • // Receive a push messsage.
    client.on("message", (topic, message, packet) => {
      if (topic === mqttTopic) {
        // Get the payload of the push message.
        const payload = JSON.parse(message.toString());
    
        // Get the command ID from the payload.
        const commandID = payload.commandID;
        if (commandID) {
          console.log("A message arrived: " + commandID);
        }
      }
    });
  • // Receive a push messsage.
    client.on("message", function(topic, message, packet) {
      if (topic === mqttTopic) {
        // Get the payload of the push message.
        var payload = JSON.parse(message.toString());
    
        // Get the command ID from the payload.
        var commandID = payload.commandID;
        if (commandID) {
          console.log("A message arrived: " + commandID);
        }
      }
    });

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

  • client.on("message", …) は、MQTT.js の仕様に基づく実装です。指定された関数は、MQTT.js でサーバー側からの PUBLISH コマンドを受け取ったときに呼び出されます。
  • プッシュメッセージに含まれるペイロードから commandID を取得することで、Thing 側で処理されたコマンドの ID を取得できます。なお、プッシュ通知機能を他の用途でも使用している場合は、commandID が取得されない状況を考慮する必要があります。

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

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

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

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

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

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

  • // Get the command.
    api.getCommand(commandID).then((command: ThingIF.Command) => {
      // Get each of the action results.
      for (const resultValue of command.actionResults) {
        const actionName: string = Object.keys(resultValue)[0];
        const succeeded: boolean = resultValue[actionName].succeeded;
        const errorMessage: string = resultValue[actionName].errorMessage;
      }
    }).catch((error: ThingIF.ThingIFError) => {
      // Handle the error.
    });
  • // Get the command.
    api.getCommand(commandID).then(
      function(command) {
        // Get each of the action results.
        for (var i = 0; i < command.actionResults.length; i++) {
          var resultValue = command.actionResults[i];
          var actionName = Object.keys(resultValue)[0];
          var succeeded = resultValue[actionName].succeeded;
          var errorMessage = resultValue[actionName].errorMessage;
        }
      }
    ).catch(
      function(error) {
        // Handle the error.
      }
    );

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

  • コマンド ID を引数として、ThingIFAPIapi インスタンス)の getCommand メソッドを呼び出すと、コマンドの全体をサーバーから取得できます。
  • コマンドの actionResults からアクションリザルトの配列を取得できます。配列の各要素はキーと値のペアを 1 つだけ持った Object で、キーがアクション名、値がアクションリザルトの本体です。

例えば、コマンドの実行 のサンプルコードでは、command.actionResults は以下のような Object 配列を返します。上記のサンプルコードでは、配列部分を for でループし、その内部に含まれるアクション名、成功したかどうかのフラグ、エラーメッセージを取得しています。

[
  {
    "turnPower": {
        "succeeded": true
    }
  },
  {
    "setPresetTemperature": {
      "errorMessage": "Too cold in the room",
      "succeeded": false
    }
  },
  {
    "setFanSpeed": {
      "succeeded": true
    }
  }
]

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

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

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

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

  • var callback = (error: Error, commands: ThingIF.QueryResult<ThingIF.Command>)=> {
      if (error) {
        // Handle the error.
        return;
      }
    
      for (const command of commands.results) {
        // Do something with each command.
      }
    
      // If the next page exists
      if (commands.paginationKey) {
        // Get the next page of the list.
        api.listCommands(new ThingIF.ListQueryOptions(null, commands.paginationKey), callback);
      }
    };
    
    // Get a list of commands.
    api.listCommands(new ThingIF.ListQueryOptions(), callback);
  • var callback = function(error, commands) {
      if (error) {
        // Handle the error.
        return;
      }
    
      for (var i = 0; i < commands.results.length; i++) {
        var command = commands.results[i];
        // Do something with each command.
      }
    
      // If the next page exists
      if (commands.paginationKey) {
        // Get the next page of the list.
        api.listCommands(new ThingIF.ListQueryOptions(null, commands.paginationKey), callback);
      }
    };
    
    // Get a list of commands.
    api.listCommands(new ThingIF.ListQueryOptions(), callback);

listCommands メソッドによってサーバーに登録されたコマンドを一覧取得しています。このメソッドは Promise にも対応していますが、全件取得するには再帰によるループ構造を作成する必要があるため、コールバック関数によるサンプルコードとしています。Promise によってループを実現する方法は、KiiObject の検索 の実装例などを参考にしてください。

listCommands メソッドでは、ListQueryOptions の第 2 引数によって、現在のページの状態を表します。初めにページネーションキーの指定なしで ListQueryOptions を指定すると、先頭ページが取得できます。listCommands メソッドのコールバックでコマンド一覧を取得した際、commands.paginationKey によって次のページネーションキーが返されます。これを次のページネーションキーとして指定すると、前回の続きのコマンドを取得できます。最終的に全件取得されると、コールバックの commands.paginationKey は undefined として通知されます。

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

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

コールバックの commands.results に取得できたコマンドの一覧が入っています。