# デバイスシャドウ同期

このページでは、デバイスの視点から見たシャドウ同期のフローについて説明します。起動時、望ましい状態の変更受信、および状態報告をカバーしています。

## 概要

シャドウ同期は、デバイスが切断を跨いでもクラウドで定義された望ましい状態に収束することを保証します。フローは主に以下の3つのフェーズで構成されています。

1. **起動時**：現在のシャドウを取得し、保留中の望ましい状態があるか検出する
2. **デルタ受信**：クラウドからプッシュされた変更を適用する
3. **状態報告**：デバイスの現在の状態をFleetsに報告する

## 起動フロー

デバイスが接続（または再接続）した際、通常の動作に入る前に現在のシャドウを取得する必要があります。これにより、デバイスがオフラインの間に行われた望ましい状態の変更をキャッチアップできます。

```text
デバイスがEMQXブローカーに接続
        │
        ▼
シャドウおよびコマンドトピックをサブスクライブ
        │
        ▼
GET /api/v1/thing-datas/{thingName}/shadow   (HTTPS)
        │
        ▼
デルタが空でないか？
   YES → 望ましい値をデバイスに適用
       → 新しい状態を報告（「状態報告」参照）
    NO → 通常動作を継続
```

## デルタの受信

クラウドが望ましい状態を更新すると、Fleetsは以下のトピックにパブリッシュします。

```text
$emqx/things/{thingName}/shadow/update/delta
```

デルタのペイロードには、望ましい状態と報告された状態が異なるフィールドのみが含まれます。

```json
{
  "version": 5,
  "timestamp": 1748390400,
  "state": {
    "targetTemperature": 24
  },
  "metadata": {
    "targetTemperature": { "timestamp": 1748390400 }
  }
}
```

デバイスの動作：

```text
デルタメッセージを受信
        │
        ▼
stateから変更されたフィールドを抽出
        │
        ▼
変更をデバイスのハードウェア／設定に適用
        │
        ▼
更新された状態を報告（「状態報告」参照）
```

望ましい状態全体をそのまま報告状態としてエコーしないでください。デバイスが完全に適用できない場合（例：ハードウェアの制限など）もあるため、デバイスの実際の現在状態を報告してください。

## 状態報告

デバイスの現在状態は、シャドウ更新トピックにQoS 1でパブリッシュして報告します。

```text
トピック:   $emqx/things/{thingName}/shadow/update
QoS:        1
Retain:     false
```

ペイロード例：

```json
{
  "state": {
    "reported": {
      "temperature": 21.5,
      "targetTemperature": 24,
      "mode": "cooling"
    }
  },
  "clientToken": "tok-001",
  "version": 4
}
```

Fleetsが報告を処理した後：
- 報告された状態はGreptimeDBおよびPostgreSQLに保存されます。
- デルタが再計算されます。報告状態が望ましい状態と一致すると、デルタは空になります。
- シャドウのバージョンがインクリメントされます。

## 望ましい状態と報告状態が一致した場合

デバイスの報告状態がクラウドの望ましい状態と一致すると、デルタは空のオブジェクト（`{}`）になります。望ましい状態が再度変更されるまでは、デルタメッセージは発行されません。

## シャドウ状態の全体図

```text
クラウドが望ましい状態を書き込み
        │
        ▼
Fleetsがデルタ = 望ましい状態 − 報告状態を計算
        │
        ▼
FleetsがMQTT経由でデバイスにデルタをパブリッシュ
        │
        ▼
デバイスがデルタを受信 → 変更を適用
        │
        ▼
デバイスが報告状態をパブリッシュ
        │
        ▼
Fleetsが報告状態を保存
Fleetsがデルタを再計算（空になる場合あり）
```

## オフライン期間

デバイスが**永続的なMQTTセッション**（`clean session = false`）を使用している場合、デバイスがオフラインの間にパブリッシュされたデルタメッセージはブローカーにキューイングされます。再接続時に、デバイスは新しいメッセージをサブスクライブする前にキューに溜まったメッセージを受信します。

永続セッションを使用していても、起動時のGETを実行して現在のシャドウ全体を取得してください。望ましい状態の連続した迅速な更新により、最終的なデルタのみがキューに残る場合があるためです。

## バージョン競合

報告ペイロードに`version`フィールドを含め、その値がサーバーの現在バージョンと一致しない場合、Fleetsはエラー応答で更新を拒否します。これを避けるには、`version`フィールドを省略するか、報告前に現在のシャドウを取得してください。

## 次のステップ

- [MQTTインテグレーション](./mqtt_integration.md)
- [HTTPSインテグレーション](./https_integration.md)
- [デバイスシャドウ](../device_shadow.md)
