デバイスは、デバイスシャドウの desired を null にしよう。なぜなら処理がシンプルになるからだ。

IoT Coreのデバイスシャドウを扱っているとき、desiredをnullにして削除するとシンプルになるという話です。
2020.08.04

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

デバイスがIoT Coreに接続してデバイスシャドウを使っているとき、クラウドからの指示がdesiredに格納されます。 そして、デバイス側の状態がreportedに格納されます。

  • desired
    • デバイスに対して要求する状態を格納する
  • reported
    • デバイスが現在の状態を格納する
  • delta
    • desiredとreportedが異なる場合に存在する(AWS自動付与)
    • 正確な仕様はドキュメントを参照

クラウドがデバイスに対して依頼を行ったとき、デバイスはdesirednullにしようという話をしていきます。

デバイスシャドウの内容をシミュレーションしてみる

任意に光るLEDを持つデバイス(led-device)があるとします。このデバイスは下記のトピックをSubscribeしており、クラウドからの依頼を反映します。

  • トピック: $aws/things/led-device/shadow/update/delta

なお、このデバイスはユーザも任意にLEDの色を変更可能とします。

1. 現在のLEDの状態は赤色とする

現在のLEDの状態は赤色です。

デバイスシャドウの様子

{
  "reported": {
    "led": "red"
  }
}

2. クラウドが「LEDを緑にしてね」と依頼する

このデバイスに対して、クラウドから「LEDを緑色にしてね」と依頼します。

  • トピック: $aws/things/led-device/shadow/update

クラウドがトピックにPublishするデータ

{
    "state": {
        "desired": {
            "led": "green"
        }
    }
}

依頼後のデバイスシャドウは下記です。

デバイスシャドウの様子

{
  "desired": {
    "led": "green"
  },
  "reported": {
    "led": "red"
  }
}

そして、$aws/things/led-device/shadow/update/deltaに来たデータは下記です。デバイスはこのデータを元にLEDを緑に変更します。

$aws/things/led-device/shadow/update/deltaに来たデータ

{
  "version": 3,
  "timestamp": 1596512574,
  "state": {
    "led": "green"
  },
  "metadata": {
    "led": {
      "timestamp": 1596512574
    }
  }
}

3. LEDを緑にしたので、デバイスシャドウを更新する

LEDを緑に変更したので、デバイスはデバイスシャドウを更新します。

  • トピック: $aws/things/led-device/shadow/update

デバイスがトピックにPublishするデータ

{
    "state": {
        "reported": {
            "led": "green"
        }
    }
}

更新後のデバイスシャドウは下記です。

デバイスシャドウの様子

{
  "desired": {
    "led": "green"
  },
  "reported": {
    "led": "green"
  }
}

差分がないため、$aws/things/led-device/shadow/update/deltaに対してデータは来ません。

4. ユーザがLEDの色を青に変更したので、デバイスシャドウを更新する

ユーザがLEDを青に変更したので、デバイスはデバイスシャドウを更新します。

  • トピック: $aws/things/led-device/shadow/update

デバイスがトピックにPublishするデータ

{
    "state": {
        "reported": {
            "led": "blue"
        }
    }
}

更新後のデバイスシャドウは下記です。

デバイスシャドウの様子

{
  "desired": {
    "led": "green"
  },
  "reported": {
    "led": "blue"
  }
}

$aws/things/led-device/shadow/update/deltaにデータが来ました。……来ちゃいました。

$aws/things/led-device/shadow/update/deltaに来たデータ

{
  "version": 5,
  "timestamp": 1596513537,
  "state": {
    "led": "green"
  },
  "metadata": {
    "led": {
      "timestamp": 1596512574
    }
  }
}

5. デバイスは、deltaの内容に従ってLEDを更新……していいの?

このとき、LEDを最後に変更したのはユーザであり、その際の色は青でした。 そのため/update/deltaの内容に従ってLEDを緑に変更する動作は求めていません。

デバイスはどのように動作すれば良いでしょうか?

このあたりを対応する方法のひとつとして、下記のトピックに空データをPublishして最新のデバイスシャドウを取得します。

  • トピック: $aws/things/led-device/shadow/get

取得したデバイスシャドウは下記です。このデバイスシャドウを元にして「desiredのタイムスタンプより、reportedのタイムスタンプが新しければ、クラウドの要求に従って動作したものとみなす」という判断をすることは可能です。(ただし、クラウドの要求を反映したのかユーザの要求を反映したのかは分からない)

デバイスシャドウの全データ

{
  "state": {
    "desired": {
      "led": "green"
    },
    "reported": {
      "led": "blue"
    },
    "delta": {
      "led": "green"
    }
  },
  "metadata": {
    "desired": {
      "led": {
        "timestamp": 1596512574
      }
    },
    "reported": {
      "led": {
        "timestamp": 1596513537
      }
    }
  },
  "version": 5,
  "timestamp": 1596514164
}

とはいえ、こんなことをするのはめんどくさいです。そこで出てくるのがdesiredをnullにしようという話です。

デバイスシャドウの内容をシミュレーションしてみる(desiredをnullにする版)

2番目まで同じ動作を行います。

1. 現在のLEDの状態は赤色とする

下記のデバイスシャドウがあり、現在のLEDの状態は赤色です。

デバイスシャドウの様子

{
  "reported": {
    "led": "red"
  }
}

2. クラウドが「LEDを緑にしてね」と依頼する

このデバイスに対して、クラウドから「LEDを緑色にしてね」と依頼します。

  • トピック: $aws/things/led-device/shadow/update

クラウドがトピックにPublishするデータ

{
    "state": {
        "desired": {
            "led": "green"
        }
    }
}

依頼後のデバイスシャドウは下記です。

デバイスシャドウの様子

{
  "desired": {
    "led": "green"
  },
  "reported": {
    "led": "red"
  }
}

そして、/update/deltaに来たデータは下記です。デバイスはこのデータを元にLEDを緑に変更します。

$aws/things/led-device/shadow/update/deltaに来たデータ

{
  "version": 7,
  "timestamp": 1596515708,
  "state": {
    "led": "green"
  },
  "metadata": {
    "led": {
      "timestamp": 1596515708
    }
  }
}

3. LEDを緑にしたので、デバイスシャドウを更新する(一緒にdesiredを削除する)

LEDを緑に変更したので、デバイスはデバイスシャドウを更新します。このとき、desiredの値をnullにして削除します。

  • トピック: $aws/things/led-device/shadow/update

デバイスがトピックにPublishするデータ

{
    "state": {
        "desired": {
            "led": null
        },
        "reported": {
            "led": "green"
        }
    }
}

更新後のデバイスシャドウは下記です。desiredが消えています。

デバイスシャドウの様子

{
  "reported": {
    "led": "green"
  }
}

差分がないため、$aws/things/led-device/shadow/update/deltaに対してデータは来ません。

4. ユーザがLEDの色を青に変更したので、デバイスシャドウを更新する

ユーザがLEDを青に変更したので、デバイスはデバイスシャドウを更新します。

  • トピック: $aws/things/led-device/shadow/update

デバイスがトピックにPublishするデータ

{
    "state": {
        "reported": {
            "led": "blue"
        }
    }
}

更新後のデバイスシャドウは下記です。スッキリしていますね。

デバイスシャドウの様子

{
  "reported": {
    "led": "blue"
  }
}

差分がないため、$aws/things/led-device/shadow/update/deltaに対してデータは来ません。

先ほどと異なり、デバイスは迷うこと無くシンプルに処理ができました。

さいごに

デバイスシャドウにdesiredが常に残り続けているとき、「クラウドが依頼したけどまだ未反映」なのか「デバイスは対応したけど、単に残っているだけ」なのかがパット見でも分かりません。何かあったときに人間が見ても分かりにくいですし、デバイスでロジックを組むにしても煩雑化して不具合の原因にもなりかねません。

デバイス側でdesirednullにすると処理がシンプルになります。

参考