コマンドの実行

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

コマンドの実行

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

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

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

// Create an array for actions to be executed as a command.
var actions = [Dictionary<String, AnyObject>]()

// Create actions.
let action1: Dictionary<String, AnyObject> = ["turnPower": ["power": true]]
let action2: Dictionary<String, AnyObject> = ["setPresetTemperature": ["presetTemperature": 25]]
let action3: Dictionary<String, AnyObject> = ["setFanSpeed": ["fanSpeed": 5]]

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

// Execute the command.
api.postNewCommand("AirConditioner-Demo", schemaVersion: 1, actions: actions, completionHandler: { (command: Command?, error: ThingIFError?)-> Void in
  if error != nil {
    // Handle the error.
    return
  }
})

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

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

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

    actions はアクションの配列です。各アクションは、アクション名とそのパラメータの組を表す Dictionary<String, AnyObject> で表現され、キーと値は 1 組だけを格納します。パラメータは AnyObject で定義されますが、ここでは Dictionary<String, AnyObject> 相当の値を AnyObject のパラメータとして設定しています。以下に構成を示します。

    アクションのパラメータの形式は任意です。SDK の内部では、NSJSONSerialization.dataWithJSONObject によってパラメータを JSON 文字列に変換します。Thing 側で想定しているとおりに変換できれば、値の形式は問いません。たとえば、上記の action1 で Thing の受け取る情報は、アクション名 turnPower に対して、パラメータが {"power":true} となります。

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

    なお、このサンプルでは説明のためにアクションを分割して定義していますが、swift の言語仕様のとおり、以下のように 1 行で記述しても構いません。

    let actions: [Dictionary<String, AnyObject>] = [["turnPower": ["power": true]],["setPresetTemperature": ["presetTemperature": 25]],["setPresetTemperature": ["presetTemperature": 25]]]
    
  2. コマンドを実行します。

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

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

コールバックで通知された後の処理の流れは以下のとおりです。

コマンドを Thing Interaction Framework が受け取ってコマンドを受け付けると、コールバックが呼び出されます。この状態ではまだ 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.
let actions: [Dictionary<String, AnyObject>] = [["turnPower": ["power": false]]]

// Execute the command.
api.postNewCommand("AirConditioner-Demo", schemaVersion: 1, actions: actions, completionHandler: { (command: Command?, error: ThingIFError?)-> Void in
  if error != nil {
    // Handle the error.
    return
  }
})

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

詳細情報をつけて送信

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

// Create an array for actions to be executed as a command.
var actions = [Dictionary<String, AnyObject>]()

// Create actions.
let action1: Dictionary<String, AnyObject> = ["turnPower": ["power": true]]
let action2: Dictionary<String, AnyObject> = ["setPresetTemperature": ["presetTemperature": 25]]
let action3: Dictionary<String, AnyObject> = ["setFanSpeed": ["fanSpeed": 5]]

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

// Set a title, description, and metadata to be sent with a command.
let title = "Power on"
let description = "Power on and set to 25 deg C"
let metadata: Dictionary<String, AnyObject> = ["iconIndex":3, "issuer":"remoteController"]

// Create a command form from the detailed information.
let commandForm = CommandForm(schemaName: "AirConditioner-Demo", schemaVersion: 1, actions: actions, title: title, commandDescription: description, metadata: metadata)

// Execute the command with the command form.
api.postNewCommand(commandForm, completionHandler: { (command: Command?, error: ThingIFError?)-> Void in
  if error != nil {
    // Handle the error.
    return
  }
})

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

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

コマンドリザルトの受信

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

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

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

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

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
  // Get the command ID from the payload.
  let returnedID = userInfo["commandID"] as? String

  if returnedID != nil {
    // Do something with the returned ID.
  }
}

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

  • application:didReceiveRemoteNotification メソッドは、APNs でプッシュ通知を受け取ったときに呼び出される iOS のメソッドです。ここにコマンドの完了通知のハンドラーを記述します。
  • プッシュメッセージに含まれるペイロード userInfo から commandID を取得すると、Thing 側で処理されたコマンドの ID を取得できます。なお、プッシュ通知機能を他の用途でも使用している場合は、commandID が nil となることがあります。

アプリがバックグラウンドにある場合、プッシュメッセージの受信は別のメソッドで通知されます。受信メソッドの組み合わせ を参考に実装してください。

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

コマンドを処理してもプッシュ通知が届かない場合、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 からコマンドの詳細を取得することができます。これによって、コマンドリザルトの確認処理などを実装できます。

// Get the command.
api.getCommand(commandID, completionHandler: { (command: Command?, error: ThingIFError?) -> Void in
  if error != nil {
    // Handle the error.
    return
  }

  // Get each of the action results.
  let actionResults: [Dictionary<String, AnyObject>] = command!.actionResults
  for actionResult in actionResults {
    for (actionName, result) in actionResult {
      let succeeded = result["succeeded"] as? Bool
      let errorMessage = result["errorMessage"] as? String
    }
  }
})

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

  • コマンド ID を引数として、ThingIFAPIapi インスタンス)の getCommand メソッドを呼び出すと、コマンドの全体をサーバーから取得できます。
  • コマンドの actionResults からアクションリザルトの配列を取得できます。配列の各要素はキーと値のペアを 1 つだけ持った Dictionary で、キーがアクション名、値がアクションリザルトの Dictionary です。構成の例は こちらの図 をご覧ください。なお、アクション名は postNewCommand メソッドで送信したアクション名に対応します。

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

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

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

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

// Get a list of commands.
api.listCommands(nil, paginationKey: nil, completionHandler: { (commands: [Command]?, paginationKey: String?, error: ThingIFError?) -> Void in
  if error != nil {
    // Handle the error.
    return
  }

  for command in commands! {
    // Do something with each command.
  }

  // If the next page exists
  if (paginationKey != nil) {
    // Get the next page of the list.
    api.listCommands(nil, paginationKey: paginationKey, completionHandler: { (commands: [Command]?, paginationKey: String?, error: ThingIFError?) -> Void in
      if error != nil {
        // Handle the error.
        return
      }
    })
  }
})

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

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

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

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

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