

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# Device Shadow 服務文件
<a name="device-shadow-document"></a>

Device Shadow 服務遵守 JSON 規格的所有規則。值、物件與陣列均存放於裝置的影子文件中。

**Topics**
+ [影子文件範例](#device-shadow-document-syntax)
+ [文件屬性](#document-structure)
+ [差量狀態](#delta-state)
+ [版本控制影子文件](#versioning)
+ [影子文件中的用戶端字符](#client-token)
+ [空白影子文件屬性](#device-shadow-empty-fields)
+ [影子文件中的陣列值](#device-shadow-arrays)

## 影子文件範例
<a name="device-shadow-document-syntax"></a><a name="device-shadow-example"></a>

在使用 [REST API](device-shadow-rest-api.md) 或 [MQTT 發佈/訂閱訊息](device-shadow-mqtt.md)的 UPDATE、GET、DELETE 操作中，Device Shadow 服務會使用下列文件。

**Topics**
+ [請求狀態文件](#device-shadow-example-request-json)
+ [回應狀態文件](#device-shadow-example-response-json)
+ [錯誤回應文件](#device-shadow-example-error-json)
+ [影子名稱清單回應文件](#device-shadow-list-json)

### 請求狀態文件
<a name="device-shadow-example-request-json"></a>

請求狀態文件具有以下格式：

```
{
    "state": {
        "desired": {
            "attribute1": integer2,
            "attribute2": "string2",
            ...
            "attributeN": boolean2
        },
        "reported": {
            "attribute1": integer1,
            "attribute2": "string1",
            ...
            "attributeN": boolean1
        }
    },
    "clientToken": "token",
    "version": version
}
```
+ `state`：更新僅會影響所指定的欄位。一般而言，您會使用 `desired` 或 `reported` 屬性，但不會在同一個要求中使用兩者。
  + `desired`：請求在裝置中更新的狀態屬性和值。
  + `reported`：裝置回報的狀態屬性和值。
+ `clientToken`：如已使用，您可透過用戶端字符來比對請求和相應的回應。
+ `version`：若使用此項目，Device Shadow 服務只有在指定版本與其擁有的最新版本相符時，才會處理更新。

### 回應狀態文件
<a name="device-shadow-example-response-json"></a>

根據回應類型，回應狀態文件的格式如下。

#### /accepted response state document
<a name="device-shadow-example-response-json-accepted"></a>

```
{
    "state": {
        "desired": {
            "attribute1": integer2,
            "attribute2": "string2",
            ...
            "attributeN": boolean2
        }
    },
    "metadata": {
        "desired": {
            "attribute1": {
                "timestamp": timestamp
            },
            "attribute2": {
                "timestamp": timestamp
            },
            ...
            "attributeN": {
                "timestamp": timestamp
            }
        }
    },
    "timestamp": timestamp,
    "clientToken": "token",
    "version": version
}
```

#### /delta response state document
<a name="device-shadow-example-response-json-delta"></a>

```
{
    "state": {
        "attribute1": integer2,
        "attribute2": "string2",
        ...
        "attributeN": boolean2
    },
    "metadata": {
        "attribute1": {
            "timestamp": timestamp
        },
        "attribute2": {
            "timestamp": timestamp
        },
        ...
        "attributeN": {
            "timestamp": timestamp
        }
    },
    "timestamp": timestamp,
    "clientToken": "token",
    "version": version
}
```

#### /documents response state document
<a name="device-shadow-example-response-json-documents"></a>

```
{
  "previous" : {
    "state": {
        "desired": {
            "attribute1": integer2,
            "attribute2": "string2",
            ...
            "attributeN": boolean2
        },
        "reported": {
            "attribute1": integer1,
            "attribute2": "string1",
            ...
            "attributeN": boolean1
        }
    },
    "metadata": {
        "desired": {
            "attribute1": {
                "timestamp": timestamp
            },
            "attribute2": {
                "timestamp": timestamp
            },
            ...
            "attributeN": {
                "timestamp": timestamp
            }
        },
        "reported": {
            "attribute1": {
                "timestamp": timestamp
            },
            "attribute2": {
                "timestamp": timestamp
            },
            ...
            "attributeN": {
                "timestamp": timestamp
            }
        }
    },
    "version": version-1
  },
  "current": {
    "state": {
        "desired": {
            "attribute1": integer2,
            "attribute2": "string2",
            ...
            "attributeN": boolean2
        },
        "reported": {
            "attribute1": integer2,
            "attribute2": "string2",
            ...
            "attributeN": boolean2
        }
    },
    "metadata": {
        "desired": {
            "attribute1": {
                "timestamp": timestamp
            },
            "attribute2": {
                "timestamp": timestamp
            },
            ...
            "attributeN": {
                "timestamp": timestamp
            }
        },
        "reported": {
            "attribute1": {
                "timestamp": timestamp
            },
            "attribute2": {
                "timestamp": timestamp
            },
            ...
            "attributeN": {
                "timestamp": timestamp
            }
        }
    },
    "version": version
  },
  "timestamp": timestamp,
  "clientToken": "token"
}
```

#### 回應狀態文件屬性
<a name="device-shadow-example-response-json-properties"></a>
+ `previous`：成功更新之後，會在更新前包含物件的 `state`。
+ `current`：成功更新之後，會在更新後包含物件的 `state`。
+ `state`
  + `reported`：僅當物件於 `reported` 區段回報任何資料，且包含僅位於請求狀態文件中的欄位時才出現。
  + `desired`：僅當裝置於 `desired` 區段回報任何資料，且包含僅位於請求狀態文件中的欄位時才出現。
+ `metadata`：包含 `desired` 和 `reported` 部分中每個屬性的時間戳記，因此您可確定狀態的更新時間。
+ `timestamp` — 產生回應的 Epoch 日期和時間 AWS IoT。
+ `clientToken`：僅在發佈有效 JSON 至 `/update` 主題並使用用戶端符記時才會出現。
+ `version`： AWS IoT中目前共用之裝置影子的文件版本。此版本會在先前的文件版本編號加一。

### 錯誤回應文件
<a name="device-shadow-example-error-json"></a>

錯誤回應文件的格式如下：

```
{
    "code": error-code,
    "message": "error-message",
    "timestamp": timestamp,
    "clientToken": "token"
}
```
+ `code`：用於指明錯誤類型的 HTTP 回應碼。
+ `message`：可提供額外資訊的文字訊息。
+ `timestamp` — 產生回應的日期和時間 AWS IoT。此屬性不會出現在所有錯誤回應文件中。
+ `clientToken`：僅於已發佈的訊息中使用用戶端字符時才會出現。

如需詳細資訊，請參閱[Device Shadow 錯誤訊息](device-shadow-error-messages.md)。

### 影子名稱清單回應文件
<a name="device-shadow-list-json"></a>

影子名稱清單回應文件的格式如下：

```
{
    "results": [
        "shadowName-1",
        "shadowName-2",
        "shadowName-3",
        "shadowName-n"
    ],
    "nextToken": "nextToken",
    "timestamp": timestamp
}
```
+ `results`：影子名稱的陣列。
+ `nextToken`：在分頁請求中使用的字符值，以取得序列中的下一頁。當沒有更多的影子名稱返回時，這個屬性不存在。
+ `timestamp` — 產生回應的日期和時間 AWS IoT。

## 文件屬性
<a name="document-structure"></a>

裝置的影子文件具有下列屬性：

`state`  <a name="state"></a>  
`desired`  <a name="desired"></a>
裝置所需的狀態。應用程式可將此部分編寫入文件，在無需直接連線的狀況下更新裝置狀態。  
`reported`  <a name="reported"></a>
裝置回報的狀態。裝置會在文件中編寫此部分以回報其新狀態。應用程式會讀取文件的這部分，以判斷裝置上次報告的狀態。

`metadata`  <a name="metadata"></a>
存放於文件 `state` 部分之資料的相關資訊。其中包括針對 `state` 部分中各屬性的時間戳記 (Epoch 時間)，可讓您確定資料是在何時更新的。  
中繼資料不會影響服務限制或定價的文件大小。如需詳細資訊，請參閱 [AWS IoT 服務限制](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#limits_iot)。

`timestamp`  <a name="timestamp"></a>
指出訊息的傳送者。 AWS IoT藉由使用訊息中的時間戳記和 `desired` 或 `reported` 區段中的個別屬性的時間戳記，裝置可以判斷屬性的保留天數，即使裝置沒有內部時鐘也是如此。

`clientToken`  <a name="clientToken"></a>
裝置特有的字串能夠讓您在 MQTT 環境中將回應與請求建立關聯。

`version`  <a name="version"></a>
文件版本。每次文件更新時，版本編號便會自動增加。如此可確保更新的文件為最新版本。

如需詳細資訊，請參閱[影子文件範例](#device-shadow-document-syntax)。

## 差量狀態
<a name="delta-state"></a><a name="observing-state-changes"></a>

差量 (delta) 狀態是一種虛擬類型的狀態，包含了 `desired` 和 `reported` 之間的差異。在 `desired` 部分的欄位若未出現於 `reported` 部分，則會納入差量之中。在 `reported` 部分的欄位若未出現於 `desired` 部分，則不會納入差量之中。差量包含中繼資料，且其值與 `desired` 欄位裡的中繼資料相同。例如：

```
{
  "state": {
    "desired": {
      "color": "RED",
      "state": "STOP"
    },
    "reported": {
      "color": "GREEN",
      "engine": "ON"
    },
    "delta": {
      "color": "RED",
      "state": "STOP"
    }
  },
  "metadata": {
    "desired": {
      "color": {
        "timestamp": 12345
      },
      "state": {
        "timestamp": 12345
      }
      },
      "reported": {
        "color": {
          "timestamp": 12345
        },
        "engine": {
          "timestamp": 12345
        }
      },
      "delta": {
        "color": {
          "timestamp": 12345
        },
        "state": {
          "timestamp": 12345
        }
      }
    },
    "version": 17,
    "timestamp": 123456789
  }
}
```

當巢狀物件不同，差量則會包含根路徑。

```
{
  "state": {
    "desired": {
      "lights": {
        "color": {
          "r": 255,
          "g": 255,
          "b": 255
        }
      }
    },
    "reported": {
      "lights": {
        "color": {
          "r": 255,
          "g": 0,
          "b": 255
        }
      }
    },
    "delta": {
      "lights": {
        "color": {
          "g": 255
        }
      }
    }
  },
  "version": 18,
  "timestamp": 123456789
}
```

Device Shadow 服務會重複查看 `desired` 狀態內的所有欄位，然後將其與 `reported` 狀態相比較，藉此計算出差量。

陣列的處理和值相同。如果 `desired` 部分的某個陣列與 `reported` 部分的陣列不符，則整組所需陣列都會複製到差量中。

## 版本控制影子文件
<a name="versioning"></a>

Device Shadow 服務支援每個更新訊息的版本控制，包括請求和回應。這表示，隨著影子的每次更新，JSON 文件的版本都會遞增。如此可確保兩種情況：
+ 如果用戶端嘗試以較舊的版本編號覆寫影子，就會收到錯誤訊息。通知用戶端必須先重新同步裝置的影子，才可進行更新。
+ 如果此訊息的版本低於存放於用戶端的訊息版本，則用戶端可以決定不採取行動。

用戶端可以透過不在影子文件中包含版本來略過版本比對。

## 影子文件中的用戶端字符
<a name="client-token"></a>

您可以使用用戶端符記收發以 MQTT 為基礎的訊息，藉此驗證請求與針對請求的回應中是否包含了同樣的用戶端符記。如此可確保回應與請求之間具有關聯。

**注意**  
用戶端字符不能超過 64 位元組。長於 64 位元組的用戶端字符會導致 400 (Bad Request) 回應以及一個 *Invalid clientToken* 錯誤訊息。

## 空白影子文件屬性
<a name="device-shadow-empty-fields"></a>

當影子文件中的 `reported` 和 `desired` 屬性不適用於目前影子狀態時，它們可以是空的，您也可以省略它們。例如，影子文件只有在具有所需狀態時才會包含 `desired` 屬性。以下是沒有 `desired` 屬性的狀態文件的有效範例：

```
{
    "reported" : { "temp": 55 }
}
```

該 `reported` 屬性也可以是空白，例如，如果影子尚未由裝置更新：

```
{
    "desired" : { "color" : "RED" }
}
```

如果更新導致 `desired` 或 `reported` 屬性變為 null，則會從文件中移除。下面顯示了如何透過將屬性設定為 `null` 來刪除 `desired`。例如，當裝置更新其狀態時，您可能會這麼做。

```
{ 
    "state": {
        "reported": {
            "color": "red" 
        }, 
        "desired": null 
    } 
}
```

影子文件也可以具有 `desired` 或 `reported` 屬性，使影子文件變成空白。這是一個空白但有效的影子文件範例。

```
{
}
```

## 影子文件中的陣列值
<a name="device-shadow-arrays"></a>

影子支援陣列，但會將其視為一般值，即更新某陣列會取代整組陣列。您無法只更新陣列中的某部分。

初始狀態：

```
{
    "desired" : { "colors" : ["RED", "GREEN", "BLUE" ] }
}
```

更新：

```
{
    "desired" : { "colors" : ["RED"] }
}
```

最終狀態：

```
{
    "desired" : { "colors" : ["RED"] }
}
```

陣列不能有 Null 值。例如，以下陣列無效並且會被拒絕。

```
{
    "desired" : { 
        "colors" : [ null, "RED", "GREEN" ]
    }
}
```