AWS IoT のコマンド機能で実行結果の追加情報を送信する

AWS IoT のコマンド機能で実行結果の追加情報を送信する

Clock Icon2025.04.03

AWS IoT にはデバイスに簡単にコマンドを送ったりステータスを管理できる 「コマンド」 という機能があります。
https://dev.classmethod.jp/articles/commands-feature-aws-iot-device-management/

この機能がリリースされた当時、基本的な使い方を上記ブログでご紹介しましたが、コマンドの実行結果の送り方がよく分からないままとなっていました。
改めてドキュメントを見ると、仕様の説明が増えていたのであらためて確認できた内容をご紹介したいと思います。

AWS IoT の「コマンド」の実行結果とは

コマンド機能では、クラウドからあらかじめ決めたコマンドをデバイスに送信して、デバイス上でそのコマンドを実行させることができます。
このとき、クラウドに送るレスポンスにはコマンドのステータス(成功、失敗など)に関する情報の他に、コマンドの実行結果をオプションで含めることができます。

この「実行結果」とは、コマンド実行時のデバイス上の関数呼び出しの戻り値など、コマンド実行の結果に関する情報のことです。
例えば、「バッテリー残量を確認する」コマンドを送信した時は「バッテリー残量(%)」を結果として送るようなケースが考えられます。

コマンド実行結果の更新

デバイスがコマンドを実行した結果を AWS IoT に通知するには、コマンド実行の 「ステータス」と「結果」 を次のレスポンストピックに MQTT で Publish します。

$aws/commands/<devices>/<DeviceID>/executions/<ExecutionId>/response/<PayloadFormat>

このトピックにメッセージが送られると、UpdateCommandExecution API が呼ばれてコマンドの実行ステータスを更新する仕組みになっています。

デバイスの IoT ポリシーに この API の実行権限を付与する必要はありません。関係するトピックに対する下記アクションの許可があれば問題ありません。

  • iot:Publish
  • iot:Receive
  • iot:Subscribe
  • iot:Connect

詳細は下記のドキュメントにポリシーのサンプルが記載されています。

https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-remote-command-execution-start-monitor.html#iot-remote-command-execution-update

このときデバイスから送るペイロードの result フィールドを使うことで、ステータスの更新時に 「実行結果」を AWS IoT に渡すことができます。
result フィールドではエントリーをキーと値のペアとして指定します。エントリーのデータ型で利用できるのは次の3種類です。 キーは小文字である必要があります。

データ型 キー
文字列 s
boolean b
バイナリ bin

なお、1つのエントリー中に複数のデータ型を指定することはできません。複数のデータ型のデータを「結果」として返したい場合は、エントリーを増やすことで対応できます。

下記にいくつかの例を記載しました。参考にしてください。

レスポンスのペイロード例

OK なケース(UpdateCommandExecution API が正常に実行できるケース)

「実行結果」を含んだペイロード例をいくつか紹介します。
下記は 公式ドキュメントにあるサンプルです。

{
  "status": "IN_PROGRESS",
  "statusReason": {
    "reasonCode": "200",
    "reasonDescription": "Execution_in_progress"
  },
  "result": {
        "car_battery": {
            "s": "car battery at 50 percent"
        }
    }
}

https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-remote-command-execution-start-monitor.html

このサンプルでレスポンスを返すと次のような結果として取り込まれます。

{
   "car_battery":{
      "S":"car battery at 50 percent",
      "B":null,
      "BIN":null
   }
}

コンソール上では1行で表示されます。

vscode-drop-1743671355576-n064z8npp8.png

1種類のデータ型で複数のメッセージを渡したい場合は、異なるエントリー名を使って必要な数だけエントリーを追加します。(文字列情報が2つの場合)

{
  "status": "SUCCEEDED",
  "statusReason": {
    "reasonCode": "200",
    "reasonDescription": "Execution_completed"
  },
  "result": {
        "car_battery": {
            "s": "car battery at 50 percent"
        },
        "car_temperature": {
            "s": "car temperature at 23.1 C"
        }
    }
}

取り込まれる「実行結果」です。

{
   "car_battery":{
      "S":"car battery at 50 percent",
      "B":null,
      "BIN":null
   },
   "car_temperature":{
      "S":"car temperature at 23.1 C",
      "B":null,
      "BIN":null
   }
}

複数種類のデータ型を使う場合はそれぞれのエントリーでデータ型を分けて記述します。(文字列と Boolean の2つの場合)

{
  "status": "SUCCEEDED",
  "statusReason": {
    "reasonCode": "200",
    "reasonDescription": "Execution_completed"
  },
  "result": {
        "car_battery": {
            "s": "car battery at 50 percent"
        },
        "car_cooling": {
            "b": true
        }
    }
}

取り込まれる「実行結果」です。

{
   "car_battery":{
      "S":"car battery at 50 percent",
      "B":null,
      "BIN":null
   },
   "car_cooling":{
      "S":null,
      "B":true,
      "BIN":null
   }
}

NG なケース(UpdateCommandExecution API の実行が失敗するケース)

1つのエントリーに複数のキーを入れることはできません。

{
  "status": "SUCCEEDED",
  "result": {
      "KeyName": {
          "s": "car battery at 50 percent",
          "b": true,
          "bin": null
      }
  },
  "statusReason": {
    "reasonCode": "200",
    "reasonDescription": "Car battery is lower than 70 percent"
  }
}

このような場合は次のようなメッセージが返ってきます。
コマンド実行結果の入力は1つだけ必要と書かれています。

{
  "error": "InvalidRequest",
  "errorMessage": "One and only one input in command execution result is required.",
  "executionId": "<ExecutionId>"
}

エラーメッセージは下記のトピックで確認できます。

$aws/commands/<devices>/<DeviceID>/executions/<ExecutionId>/response/rejected/<PayloadFormat>

同じデータ型の場合は1つだけ送られる

同じエントリー名で複数のデータを送ると、実行結果として記録されるのは1つだけです。
下記の内容で実行結果を送信してみます。

{
  "status": "SUCCEEDED",
  "statusReason": {
    "reasonCode": "200",
    "reasonDescription": "Execution_completed"
  },
  "result": {
        "car_status": {
            "s": "car battery at 50 percent"
        },
        "car_status": {
            "s": "car temperature at 23.1 C"
        }
    }
}

次のような結果になりました。2つのエントリーのうち1つだけが取り込まれています。

{
   "car_status":{
      "S":"car temperature at 23.1 C",
      "B":null,
      "BIN":null
   }
}

試しに次のようなレスポンスも送ってみます。

{
  "status": "SUCCEEDED",
  "statusReason": {
    "reasonCode": "200",
    "reasonDescription": "Execution_completed"
  },
  "result": {
        "car_status": {
            "s": "car battery at 50 percent"
        },
        "car_status": {
            "s": "car battery at 51 percent"
        },
        "car_status": {
            "s": "car battery at 49 percent"
        }
    }
}

結果は次のとおりです。3番目に書いたエントリーが取り込まれているようですが、確かな仕様は確認できませんでした。

{
   "car_status":{
      "S":"car battery at 49 percent",
      "B":null,
      "BIN":null
   }
}

同じエントリー名で異なるデータ型だとどうなるでしょう?

{
  "status": "SUCCEEDED",
  "statusReason": {
    "reasonCode": "200",
    "reasonDescription": "Execution_completed"
  },
  "result": {
        "car_status": {
            "s": "car battery at 50 percent"
        },
        "car_status": {
            "b": true
        }
    }
}

Boolean のデータだけになりました。

{
   "car_status":{
      "S":null,
      "B":true,
      "BIN":null
   }
}

先ほどとエントリーの中身の順番を変えてみました。

{
  "status": "SUCCEEDED",
  "statusReason": {
    "reasonCode": "200",
    "reasonDescription": "Execution_completed"
  },
  "result": {
        "car_status": {
           "b": true
        },
        "car_status": {
            "s": "car battery at 50 percent"
        }
    }
}

2番目に書いたエントリーの内容が取り込まれました。

{
   "car_status":{
      "S":"car battery at 50 percent",
      "B":null,
      "BIN":null
   }
}

実行結果のベストプラクティス

以上の検証内容から実行結果を送る場合は、次の点に注意すると良さそうです。

  • 1つのエントリーには1つのキーのみ指定する(複数のキーは指定できない)
  • 複数のデータ型をもつキーを使う場合はエントリーを分ける
  • 複数のエントリーを使う場合は重複しないエントリー名にする
  • データ型を指定するキーは小文字にする

オマケ: マネジメントコンソールの「MQTT テストクライアント」を使う場合

検証する際は、前回の記事のように MQTTX などの MQTT クライアントを使うと分かりやすいですが、AWS マネジメントコンソールで利用できる「MQTT テストクライアント」でも可能です。

AWS IoT のマネジメントコンソールから「MQTT テストクライアント」を開いて利用できます。

vscode-drop-1743672963764-6fhbxeszyq5.png

ここで少し話を戻します。記事の冒頭でコマンドの実行結果を送るためのトピックは下記の通りだと紹介しました。

$aws/commands/<devices>/<DeviceID>/executions/<ExecutionId>/response/<PayloadFormat>

この内、<devices>/<DeviceID> には、AWS IoT Core に登録している 「モノの名前」か「クライアント ID」 のいずれかを指定します。
「モノの名前」 を使う場合は、things/<モノの名前> という形になります。「クライアント ID」 を使う場合は clients/<クライアントID> となります。

マネジメントコンソールで使える「MQTT テストクライアント」は「モノ」として登録されていませんが、「クライアントID」が発行されます。 ID は画面の「クライアントID」と書かれている部分で確認できます。

vscode-drop-1743672474872-gigb7dbdgs.png

デフォルトで生成されるクライアント ID は少し長いので、今回は短いものに変更します。
画面にある 「切断」 をクリックします。これで AWS IoT Core の MQTT ブローカーとテストクライントが切断されます。

vscode-drop-1743673167888-xeit2ype6ps.png

切断されるとクライアント ID を編集できるので、適当なものに変更します。今回は my-iotconsole としました。
(「新しいクライアント ID の生成」をクリックすると、変更前と同じフォーマットで新しい ID がセットされます。)

vscode-drop-1743673230963-02izqn7yjqtm.png

これで「コマンド」実行のリクエストが発行されるトピックが以下に決まりました。

$aws/commands/clients/my-iotconsole/executions/+/request/json

このトピックを事前にテストクライアント上でサブスクライブしておきます。

vscode-drop-1743675738582-es9sonj08ve.png

次にコマンドを実行します。コマンドは前回の記事で作ったものを流用します。

vscode-drop-1743673775033-ttmu69bdcn.png

コマンド実行の設定画面では、対象デバイスに「クライアント ID」を選択して my-iotconsole と入力します。
実行タイムアウトは長めに 10 分を指定しておきます。

vscode-drop-1743674187664-zexxtmjmkka.png

コマンドを実行すると、事前にサブスクライブしておいた下記のトピックに「コマンドID」を含んだメッセージを受信します。

$aws/commands/clients/my-iotconsole/executions/+/request/json

「コマンド ID」(bc7b6fcb-bbf5-4dec-9402-a719c2a65569)は後で使うので控えておきましょう。

vscode-drop-1743674431349-iwv0gh8zrd.png

これでデバイス(MQTT テストクライアント)がコマンドを受け付けた状態になっているので、コマンドの実行が正常に完了したと仮定して、そのレスポンスと結果を返します。送信先のトピックは次のとおりです。
トピックに先ほど控えた「コマンドID」を指定して、どのコマンドに対するレスポンスなのかを指定して結果を送信します。

$aws/commands/clients/my-iotconsole/executions/bc7b6fcb-bbf5-4dec-9402-a719c2a65569/response/json

メッセージの送信(Publish)は MQTT テストクライアントから行うので、「トピックに公開する」タブを開いて、上記のトピックとメッセージを入力します。メッセージは画面のとおりです。

vscode-drop-1743675386320-n8adyswl84o.png

レスポンスを 正常に AWS IoT が受け取れば、下記のようにコマンド履歴が表示されます。

vscode-drop-1743674800560-8fg3bwxfwzs.png

コマンド履歴を開くと、先ほど送った「実行結果」が確認できました。

vscode-drop-1743674806950-7gmhhl5k3te.png

最初に

ようやく「コマンド実行結果」に関する仕様が把握できたので、気になっていた点を色々と試すことができました。

この記事がどなたかのお役に立てれば幸いです。

IoT 導入支援 / 相談会の紹介

クラスメソッドでは、AWS を中心とした IoT 導入においてエッジからクラウドまでトータルでサポートしています。

次のようなお困りごとがあれば、お気軽にお問い合わせください。

  • ローカルでデータ収集と可視化はできたが、クラウドで可視化する適切な構成が分からない
  • 集めたデータを使って故障予知などに使いたい
  • 工場設備からデータ収集してクラウドに送る方法が分からない
  • スマートファクトリー化を検討しているが何から着手していいか分からない
  • デバイスで動くアプリケーションの開発を AWS で効率化したい
  • エッジデバイスのセキュリティが心配だ
  • カメラ映像をクラウドにストリーミングして活用したい
  • その他、お困り事があれば何でもご相談ください

https://classmethod.jp/aws/services/consulting/

https://classmethod.jp/seminar/aws-consultation/

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.