

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

# AWS IoT Device Shadow 服務
<a name="iot-device-shadows"></a>

 AWS IoT Device Shadow 服務會將陰影新增至 AWS IoT 物件。影子可讓應用程式和其他服務使用裝置的狀態，無論裝置是否連接到 AWS IoT 。 AWS IoT 物件可以有多個具名影子，讓您的 IoT 解決方案有更多選項可將您的裝置連接到其他應用程式和服務。

AWS IoT 物件在明確建立之前沒有任何陰影。您可以使用 AWS IoT 主控台建立、更新和刪除陰影。裝置、其他 Web 用戶端和服務可使用 MQTT 及[預留的 MQTT 主題](reserved-topics.md#reserved-topics-shadow)、使用 [Device Shadow REST API](device-shadow-rest-api.md) 的 HTTP，以及 [AWS IoT的AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iot-data/index.html)。由於陰影由 存放在 AWS 雲端，因此無論裝置是否已連線，它們都可以從應用程式和其他雲端服務收集和報告裝置狀態資料。

## 使用影子
<a name="device-shadow-using"></a>

影子會提供可靠的資料存放區，讓裝置、應用程式和其他雲端服務共用資料。它們可讓裝置、應用程式和其他雲端服務連線和中斷連線，而不會遺失裝置的狀態。

當裝置、應用程式和其他雲端服務連線時 AWS IoT，他們可以透過其影子來存取和控制裝置的目前狀態。例如，應用程式可以透過更新影子來請求裝置狀態的變更。 會 AWS IoT 發佈訊息，指出裝置變更。裝置會接收此訊息、更新其狀態以符合，並發佈具有更新狀態的訊息。Device Shadow 服務會在對應的影子中反映此更新的狀態。應用程式可以訂閱影子的更新，也可以查詢影子的目前狀態。

當裝置離線時，應用程式仍可與 AWS IoT 和裝置的陰影通訊。當裝置重新連線時，它會收到其影子的目前狀態，以便更新其狀態以符合其影子的狀態，然後發佈具有更新狀態的訊息。同樣地，當應用程式離線且裝置狀態在離線時變更時，裝置會保持影子更新，以便在重新連線時，應用程式可以查詢其目前狀態的影子。

如果您的裝置頻繁離線，而您希望將裝置設定為在重新連線後接收增量訊息，則可以使用持續工作階段功能。如需持續工作階段有效期限的詳細資訊，請參閱[持續工作階段有效期限](https://docs.aws.amazon.com//general/latest/gr/iot-core.html#message-broker-limits)。

### 選擇使用已命名或未命名的影子
<a name="iot-device-shadow-named"></a>

Device Shadow 服務支援已命名、未命名或傳統的影子。一個物件可以有多個已命名的影子，並且不超過一個未命名的影子。物件也可以具有預留已命名影子，其運作方式與已命名影子類似，只是您無法更新其名稱。如需詳細資訊，請參閱[預留已命名影子](https://docs.aws.amazon.com/iot/latest/developerguide/preparing-to-use-software-package-catalog.html#reserved-named-shadow)。

一個物件物件可以同時具有已命名和未命名的影子；但是用於存取每個影子的 API 略有不同，所以決定最適合您解決方案的影子類型並僅使用該類型可能會更有效率。如需存取影子 API 的詳細資訊，請參閱 [影子主題](reserved-topics.md#reserved-topics-shadow)。

使用已命名的影子，您可以建立物件物件狀態的不同檢視。例如，您可以將具有許多屬性的物件物件分割成具有邏輯屬性群組的影子，每個屬性都用其影子名稱加以識別。您也可以透過將屬性分組成不同的影子，並使用原則來控制存取來限制屬性的存取權限。如需與裝置影子搭配使用之政策的詳細資訊，請參閱[適用於 AWS IoT的動作、資源及條件索引鍵](https://docs.aws.amazon.com//service-authorization/latest/reference/list_awsiot.html)及 [AWS IoT Core 政策](https://docs.aws.amazon.com//iot/latest/developerguide/iot-policies.html)。

典型、未命名的影子比已命名的影子更簡單，但限制比已命名的影子多。每個 AWS IoT 物件只能有一個未命名的影子。如果您預期 IoT 解決方案對影子資料的需求有限，這可能就是您要開始使用影子的契機。但是，如果您認為未來可能想要增加其他影子，請考慮從一開始就使用已命名的影子。

機群索引以不同方式支援未命名的影子和已命名的影子。如需詳細資訊，請參閱[管理機群索引](managing-fleet-index.md)。

### 存取影子
<a name="device-shadow-using-access"></a>

每個影子都有預留的 [MQTT 主題](reserved-topics.md#reserved-topics-shadow)和 [HTTP URL](device-shadow-rest-api.md)，其支援在影子上的 `get`、`update` 和 `delete` 動作。

影子會使用 [JSON 影子文件](device-shadow-document.md) 來儲存和擷取資料。影子的文件包含狀態屬性，描述裝置狀態的下列層面：
+ `desired`

  應用程式會透過更新 `desired` 物件來指定裝置屬性的所需狀態。
+ `reported`

  裝置會報告 `reported` 物件中的目前狀態。
+ `delta`

  AWS IoT 會報告 `delta` 物件中所需狀態與報告狀態之間的差異。

儲存在影子中的資料會由更新動作訊息內文的狀態屬性決定。後續的更新動作可以修改現有資料物件的值，也可以在影子的狀態物件中新增和刪除金鑰和其他元素。如需存取影子的詳細資訊，請參閱 [在裝置中使用影子](device-shadow-comms-device.md) 和 [在應用程式和服務中使用影子](device-shadow-comms-app.md)。

**重要**  
提出更新要求的權限應限於受信任的應用程式和裝置。這可以防止影子的狀態屬性遭意外地變更；否則，使用影子的裝置和應用程式的設計應預期狀態屬性中的金鑰會變更。

### 在裝置、應用程式和其他雲端服務中使用影子
<a name="device-shadow-implementing"></a>

在裝置、應用程式和其他雲端服務中使用影子需要一致性和協調性。 AWS IoT Device Shadow 服務會儲存陰影狀態、在陰影狀態變更時傳送訊息，以及回應變更其狀態的訊息。IoT 解決方案中的裝置、應用程式和其他雲端服務必須管理其狀態，並使其與 Device Shadow 狀態保持一致。

影子狀態資料是動態的，可由裝置、應用程式和其他具有存取影子權限的雲端服務進行變更。基於這個原因，請務必考慮每個裝置、應用程式和其他雲端服務如何與影子互動。例如：
+ 將狀態資料傳到影子時，*設備*應該只會寫入影子狀態的 `reported` 屬性。
+ 透過影子將狀態變更要求傳給裝置時，*應用程式和其他雲端服務*應該只會寫入 `desired` 屬性。

**重要**  
包含在影子資料物件中的資料是獨立於其他影子和其他物件物件屬性的資料，例如物件的屬性和物件物件的裝置可能發佈的 MQTT 訊息內容。但如有必要，裝置可以在不同的 MQTT 主題和影子中報告相同的資料。  
支援多重影子的裝置必須維持它在不同影子中報告之資料的一致性。

### 訊息順序
<a name="message-ordering"></a>

我們無法保證服務的訊息 AWS IoT 會以任何特定順序送達裝置。下列情境說明在此情況下會發生的情況。

初始狀態文件：

```
{
  "state": {
    "reported": {
      "color": "blue"
    }
  },
  "version": 9,
  "timestamp": 123456776
}
```

更新 1：

```
{
  "state": {
    "desired": {
      "color": "RED"
    }
  },
  "version": 10,
  "timestamp": 123456777
}
```

更新 2：

```
{
  "state": {
    "desired": {
      "color": "GREEN"
    }
  },
  "version": 11,
  "timestamp": 123456778
}
```

最終狀態文件：

```
{
  "state": {
    "reported": {
      "color": "GREEN"
    }
  },
  "version": 12,
  "timestamp": 123456779
}
```

這樣會產生兩個差量訊息：

```
{
  "state": {
    "color": "RED"
  },
  "version": 11,
  "timestamp": 123456778
}
```

```
{
  "state": {
    "color": "GREEN"
  },
  "version": 12,
  "timestamp": 123456779
}
```

裝置可能不會依此順序收到這些訊息。由於這些訊息中的狀態是累積的，因此裝置可以安全捨棄比追蹤中版本編號更舊的所有訊息。如果裝置在接收到版本 11 的差量之前收到了版本 12 的差量，則可安全捨棄版本 11 的訊息。

### 裁剪影子訊息
<a name="device-shadow-trim-messages"></a>

若要減少傳送至您裝置的影子訊息大小，請定義只選擇了裝置所需之欄位的規則，然後將訊息重新發佈至裝置目前監聽的 MQTT 主題。

規則是在 JSON 中指定，並應如下所示：

```
{
  "sql": "SELECT state, version FROM '$aws/things/+/shadow/update/delta'",
  "ruleDisabled": false,
  "actions": [
    {
      "republish": {
        "topic": "${topic(3)}/delta",
        "roleArn": "arn:aws:iam:123456789012:role/my-iot-role"
      }
    }
  ]
}
```

SELECT 陳述會決定將訊息的哪些欄位重新發佈至指定主題。使用 "\$1" 萬用字元表示所有影子名稱。規則會指定所有符合的訊息都應重新發佈至指定主題。在這種情況下，該 `"topic()"` 函數是用於指定要重新發佈的主題。`topic(3)` 會評估原始主題中的物件名稱。如需關於建立規則的詳細資訊，請參閱 [的規則 AWS IoT](iot-rules.md)。

# 在裝置中使用影子
<a name="device-shadow-comms-device"></a>

本節說明使用 MQTT 訊息與陰影進行裝置通訊，MQTT 訊息是裝置與 AWS IoT Device Shadow 服務通訊的偏好方法。

影子通訊功能會模擬使用 MQTT 的發佈/訂閱通訊模型的請求/回應模型。每個影子動作包括一個請求主題，一個成功的回應主題 (`accepted`) 和一個錯誤響應主題 (`rejected`)。

如果您希望應用程式和服務能夠判斷裝置是否已連線，請參閱 [偵測裝置是否已連線](device-shadow-comms-app.md#thing-connection)。

**重要**  
因 MQTT 會使用發佈/訂閱通訊模型，因此您應*先*訂閱回應主題，再發佈請求主題。若您並未這麼做，則將不會收到發佈請求的回應。  
若您使用 [AWS IoT Device SDK](iot-sdks.md) 呼叫 Device Shadow 服務 API，則會為您處理。

本節範例使用主題的縮寫形式，其中 *ShadowTopicPrefix* 可參照已命名或未命名的影子，如本表格所述。

影子可以為已命名或未命名 (典型)。各影子所使用的主題只有在主題字首中有所不同。此表格會顯示每種影子類型所使用的主題字首。


| *ShadowTopicPrefix* 值 | 影子類型 | 
| --- | --- | 
| \$1aws/things/thingName/shadow | 未命名 (經典) 影子 | 
| \$1aws/things/thingName/shadow/name/shadowName | 已命名影子 | 

**重要**  
確保您的應用程式或服務對影子的使用符合一致性，並且受您裝置中對應的實作支援。例如，請考慮如何建立、更新和刪除影子。也請考慮如何在裝置中處理更新，以及透過影子存取裝置的應用程式或服務。您的設計應該清楚了解裝置狀態如何更新和報告，以及您的應用程式和服務如何與裝置及其影子互動。

若要建立完整的主題，請在 `ShadowTopicPrefix` 選取您要參照的影子類型、用對應值取代 `thingName` 及 `shadowName` (如適用) 的影子類型，然後將該類型附加至主題 stub，如下表所示。請記住，主題會區分大小寫。

如需有關影子保留主題的詳細資訊，請參閱 [影子主題](reserved-topics.md#reserved-topics-shadow)。

## 在第一次連線至 時初始化裝置 AWS IoT
<a name="device-shadow-comms-device-first-connect"></a>

裝置向 註冊後 AWS IoT，應該訂閱這些 MQTT 訊息，以取得其支援的陰影。


| 主題 | 意義 | 收到此主題時裝置應採取的動作 | 
| --- | --- | --- | 
|  `ShadowTopicPrefix/delete/accepted`  |  `delete` 請求已被接受並 AWS IoT 刪除影子。  |  適應已刪除影子所需的動作，例如停止發佈更新。  | 
|  `ShadowTopicPrefix/delete/rejected`  |  `delete` 請求遭到 拒絕， AWS IoT 且影子未遭到刪除。訊息內文包含錯誤資訊。  |  在訊息內文中回應錯誤訊息。  | 
|  `ShadowTopicPrefix/get/accepted`  |  `get` 請求已由 接受 AWS IoT，且訊息內文包含目前的影子文件。  |  處理訊息內文中的狀態文件所需的動作。  | 
|  `ShadowTopicPrefix/get/rejected`  |  `get` 請求遭到 拒絕 AWS IoT，且訊息內文包含錯誤資訊。  |  在訊息內文中回應錯誤訊息。  | 
|  `ShadowTopicPrefix/update/accepted`  |  `update` 請求已由 接受 AWS IoT，且訊息內文包含目前的影子文件。  |  確認訊息內文中的更新資料符合裝置狀態。  | 
|  `ShadowTopicPrefix/update/rejected`  |  `update` 請求遭到 拒絕 AWS IoT，且訊息內文包含錯誤資訊。  |  在訊息內文中回應錯誤訊息。  | 
|  `ShadowTopicPrefix/update/delta`  |  影子文件已由請求更新 AWS IoT，且訊息內文包含請求的變更。  |  更新裝置的狀態，以符合訊息內文中所需的狀態。  | 
|  `ShadowTopicPrefix/update/documents`  |  最近已完成影子的更新，而訊息內文包含目前的影子文件。  |  確認訊息內文中的更新狀態符合裝置的狀態。  | 

訂閱上表中每個影子的訊息之後，裝置應該測試，看看它支援的影子是否已經透過將 `/get` 主題發佈到每個影子來建立。如果收到 `/get/accepted` 訊息，訊息內文會包含影子文件，裝置可用來初始化其狀態。如果收到 `/get/rejected` 訊息，應該透過發佈具有目前裝置狀態的 `/update` 訊息來建立影子。

例如，假設您有一個沒有任何經典或已命名影子的物件 `My_IoT_Thing`。如果您現在於保留主題 `$aws/things/My_IoT_Thing/shadow/get` 上發佈 `/get` 請求，則會在 `$aws/things/My_IoT_Thing/shadow/get/rejected` 主題上傳回錯誤，因為物件沒有任何影子。若要解決此錯誤，請先使用 `$aws/things/My_IoT_Thing/shadow/update` 主題發佈 `/update` 訊息與目前裝置狀態 (如以下承載)。

```
{
	"state": {
		"reported": {
			"welcome": "aws-iot",
			"color": "yellow"
		}
	}
}
```

現在會為物件建立一個經典影子，並將訊息發佈至 `$aws/things/My_IoT_Thing/shadow/update/accepted` 主題。如果發佈至主題 `$aws/things/My_IoT_Thing/shadow/get`，則會將回應與裝置狀態傳回給 `$aws/things/My_IoT_Thing/shadow/get/accepted` 主題。

對於已命名的影子，在使用 get 請求之前，必須先建立已命名的影子或發佈含有影子名稱的更新。例如，若要建立已命名的影子 `namedShadow1`，請先將裝置狀態資訊發佈至主題 `$aws/things/My_IoT_Thing/shadow/name/namedShadow1/update`。若要擷取狀態資訊，請對已命名的影子 `$aws/things/My_IoT_Thing/shadow/name/namedShadow1/get` 使用 `/get` 請求。

## 在裝置連線到 時處理訊息 AWS IoT
<a name="device-shadow-comms-device-while-connected"></a>

當裝置連線到 時 AWS IoT，它可以接收 **/update/delta** 訊息，並應透過下列方式讓裝置狀態與其影子中的變更相符：

1. 讀取接收到的所有 **/update/delta** 訊息，並同步處理裝置狀態以進行比對。

1. 只要裝置狀態發生變化時，便使用具有裝置目前狀態的 `reported` 訊息內文發佈 **/update** 訊息。

當裝置連線時，它應該在指示時發佈這些訊息。


| 指示 | 主題 | 承載 | 
| --- | --- | --- | 
|  裝置的狀態已變更。  |  `ShadowTopicPrefix/update`  |  具有 `reported` 屬性的影子文件。  | 
| 裝置可能不會與影子同步。 |  `ShadowTopicPrefix/get`  | (空白) | 
|  裝置上的動作表示裝置將不再支援影子，例如移除或取代裝置時。  |  `ShadowTopicPrefix/delete`  | (空白) | 

## 當裝置重新連線至 時處理訊息 AWS IoT
<a name="device-shadow-comms-device-reconnect"></a>

當具有一或多個影子的裝置連線到 時 AWS IoT，應該透過下列方式將其狀態與其支援的所有影子狀態同步：

1. 讀取接收到的所有 **/update/delta** 訊息，並同步處理裝置狀態以進行比對。

1. 使用具有裝置目前狀態的 `reported` 訊息內文發佈 **/update** 訊息。

# 在應用程式和服務中使用影子
<a name="device-shadow-comms-app"></a>

本節說明應用程式或服務如何與 AWS IoT Device Shadow 服務互動。此範例假設應用程式或服務只透過影子與影子與該裝置互動。此範例不包含任何管理動作，例如建立或刪除影子。

此範例使用 AWS IoT Device Shadow 服務的 REST API 與陰影互動。與用於 [在裝置中使用影子](device-shadow-comms-device.md) 中的範例 (使用發佈/訂閱通訊模型) 不同，此範例使用 REST API 的請求/回應通訊模型。這表示應用程式或服務必須先提出請求，才能從中接收回應 AWS IoT。不過，此模型的缺點是它不支援通知。如果您的應用程式或服務需要及時通知裝置狀態變更，請考慮透過 WSS 通訊協定支援發佈/訂閱通訊模型的 MQTT 或 MQTT，如中所述 [在裝置中使用影子](device-shadow-comms-device.md)。

**重要**  
請確定您的應用程式或服務對影子的使用與裝置中的對應實作一致，並支援這些功能。例如，請考慮如何建立、更新和刪除影子，以及如何在裝置和存取影子的應用程式或服務中處理更新。您的設計應該清楚指定裝置狀態的更新和報告方式，以及您的應用程式和服務如何與裝置及其影子互動。

REST API 的已命名影子 URL 是：

```
https://endpoint/things/thingName/shadow?name=shadowName
```

和一個未命名的影子：

```
https://endpoint/things/thingName/shadow
```

其中：

端點  
CLI 命令傳回的端點：  

```
aws iot describe-endpoint --endpoint-type IOT:Data-ATS
```

thingName  
影子所屬物件物件的名稱

shadowName  
已命名影子的名稱。此參數不會與未命名影子搭配使用。

## 在連線至 時初始化應用程式或服務 AWS IoT
<a name="device-shadow-comms-app-first-connect"></a>

當應用程式第一次連線到 時 AWS IoT，應傳送 HTTP GET 請求至其使用的影子 URLs，以取得其使用的影子目前狀態。這可讓它將應用程式或服務同步到影子。

## 應用程式或服務連線到 時，處理狀態會變更 AWS IoT
<a name="device-shadow-comms-app-while-connected"></a>

當應用程式或服務連線到 時 AWS IoT，它可以在其使用的影子 URLs上傳送 HTTP GET 請求，以定期查詢目前狀態。

當使用者與應用程式或服務互動以變更裝置狀態時，應用程式或服務可以將 HTTP POST 請求傳送至用來更新 `desired` 影子狀態的影子 URL。此請求會傳回已接受的變更，但是您可能必須透過提出 HTTP GET 請求來輪詢影子，直到裝置已更新其新狀態的影子。

## 偵測裝置是否已連線
<a name="thing-connection"></a>

若要判斷裝置目前是否已連線，請在影子文件中包含 `connected` 屬性，並使用 MQTT Last Will 和 Testament (LWT) 訊息以將 `connected` 屬性設為 `false` (若裝置因錯誤而中斷連線)。

**注意**  
 AWS IoT Device Shadow 服務會忽略傳送至 AWS IoT 預留主題 （開頭為 \$1 的主題） 的 MQTT LWT 訊息。不過，它們是由訂閱的用戶端和 AWS IoT 規則引擎處理，因此您需要建立傳送至非預留主題的 LWT 訊息，以及將 MQTT LWT 訊息作為影子更新訊息重新發佈至影子的預留更新主題 的規則`ShadowTopicPrefix/update`。

**傳送 LWT 訊息 Device Shadow 服務**

1. 建立重新發佈保留主題上的 MQTT LWT 訊息的規則。下列範例規則會接聽有關 `my/things/myLightBulb/update` 主題的訊息，並將其重新發佈至 `$aws/things/myLightBulb/shadow/update`。

   ```
   {
       "rule": {
       "ruleDisabled": false,
       "sql": "SELECT * FROM 'my/things/myLightBulb/update'",
       "description": "Turn my/things/ into $aws/things/",
       "actions": [
           {
           "republish": {
               "topic": "$$aws/things/myLightBulb/shadow/update",
               "roleArn": "arn:aws:iam:123456789012:role/aws_iot_republish"
               }
           }
        ]
      }
   }
   ```

1. 當裝置連線到 時 AWS IoT，它會將 LWT 訊息註冊到未保留的主題，以便重新發佈規則進行辨識。在此範例中，該主題是 `my/things/myLightBulb/update`，它會將連線的內容設定為 `false`。

   ```
   {
       "state": {        
           "reported": {
               "connected":"false"
           }
       }
   }
   ```

1. 連線後，裝置會在其影子更新主題上發佈訊息 `$aws/things/myLightBulb/shadow/update`，並報告其目前狀態，其中包括將其 `connected` 屬性設定為 `true`。

   ```
   {
        "state": {        
           "reported": {
               "connected":"true"
           }
       }
   }
   ```

1. 在裝置正常中斷連線之前，它會發佈其影子更新主題的訊息 `$aws/things/myLightBulb/shadow/update`，以報告其最新狀態，其中包括將其 `connected` 屬性設定為 `false`。

   ```
   {
       "state": {        
           "reported": {
               "connected":"false"
           }
       }
   }
   ```

1. 如果裝置因錯誤而中斷連線， AWS IoT 訊息中介裝置會代表裝置發佈裝置的 LWT 訊息。重新發佈規則會偵測到此訊息，並發佈影子更新訊息，以更新 Device Shadow 的 `connected` 屬性。

**注意**  
由於中斷連線處理的非同步性質，無法保證在重新連線期間依順序分派 LWT 訊息。我們建議您使用[生命週期事件](life-cycle-events.md)來改善連線狀態偵測的準確性，因為這些事件提供屬性來管理out-of-order的事件。

# 模擬 Device Shadow 服務通訊
<a name="using-device-shadows"></a>

本主題示範如何將 Device Shadow 服務如何作為中繼點，並允許裝置和應用程式使用影子來更新、儲存和擷取裝置的狀態。

若要示範本主題中所述的互動，並進一步探索，您需要 AWS 帳戶 和 系統，才能執行 AWS CLI。如果您沒有這些項目，您仍然可以在程式碼範例中看到互動。

在此範例中， AWS IoT 主控台代表裝置。 AWS CLI 代表透過影子存取裝置的應用程式或服務。介面與應用程式可能用來通訊的 API AWS CLI 非常相似 AWS IoT。此範例中的裝置是智慧型燈泡，應用程式會顯示燈泡的狀態，而且可以變更燈泡的狀態。

## 準備模擬
<a name="using-device-shadows-setup"></a>

以下程序會開啟模擬裝置的 [AWS IoT 主控台](https://console.aws.amazon.com/iot/home)，以及模擬應用程式的命令列視窗來初始化模擬。

**準備模擬環境的步驟**

1. 您需要 AWS 帳戶 ，才能自行執行本主題中的範例。如果您沒有 AWS 帳戶，請建立一個，如 中所述[設定 AWS 帳戶](setting-up.md)。

1. 開啟 [AWS IoT 主控台](https://console.aws.amazon.com/iot/home)，然後在左側選單中選擇 **Test (測試)** 以開啟 **MQTT client (MQTT 用戶端)**。

1. 在另一個視窗中，在已安裝 AWS CLI 的系統上開啟終端機視窗。

您應該開啟兩個視窗：一個在**測試**頁面上使用 AWS IoT 主控台，另一個使用命令列提示字元。

## 初始化裝置
<a name="using-device-shadows-init-device"></a>

在這個模擬中，我們將使用一個名為 *mySimulatedThing* 的物件物件，以及其名為 *simShadow1* 的影子。

**建立物件及其 IoT 政策**  
如要建立物件物件，請於 **AWS IoT 主控台**中：

1. 選擇 **Manage** (管理)，然後選擇 **Things** (物件)。

1. 如果列出實物，請按一下**建立**按鈕，否則請按一下**註冊單一實物**來建立單一 AWS IoT 實物。

1. 輸入名稱 `mySimulatedThing`，將其他設定保留為預設值，然後按一下**Next** (下一步)。

1. 使用單鍵憑證建立來產生憑證，其會驗證裝置到 AWS IoT的連線。按一下 **Activate** (啟用)，啟用憑證。

1. 您可連接政策 `My_IoT_Policy`，其將授與裝置發佈和訂閱 MQTT 預留主題的許可。如需如何建立 AWS IoT 物件以及如何建立此政策的詳細步驟，請參閱 [建立物件](create-iot-resources.md#create-aws-thing)。

**為物件建立已命名的影子**  
您可以向主題 `$aws/things/mySimulatedThing/shadow/name/simShadow1/update` 發佈更新請求來為物件建立已命名的影子，如下方所述。

或者，若要建立具名影子：

1. 在 **AWS IoT Console** (主控台) 中，在顯示的物件清單中選擇您的物件，然後選擇 **Shadows** (影子)。

1. 選擇 **Add a shadow** (新增影子)，輸入名稱 `simShadow1`，接著選擇 **Create** (建立)，以新增命名的影子。

**訂閱並發佈至預留 MQTT 主題**  
於主控台中，訂閱預留 MQTT 影子主題。這些主題是對 `get`、`update` 和 `delete` 動作的回應，以便您的裝置在發佈動作之後就可以接收回應。

**訂閱 **MQTT 用戶端**中的 MQTT 主題**

1. 於 **MQTT client** (MQTT 用戶端) 中，選擇 **Subscribe to a topic** (訂閱主題)。

1. 輸入 `get`、`update` 和 `delete` 主題以進行訂閱。從下方清單一次複製一個主題，將其貼到**Topic filter** (主題篩選條件) 欄位，再按一下 **Subscribe** (訂閱)。您應會看到顯示於 **Subscriptions** (訂閱) 之下的主題。
   + `$aws/things/mySimulatedThing/shadow/name/simShadow1/delete/accepted`
   + `$aws/things/mySimulatedThing/shadow/name/simShadow1/delete/rejected`
   + `$aws/things/mySimulatedThing/shadow/name/simShadow1/get/accepted`
   + `$aws/things/mySimulatedThing/shadow/name/simShadow1/get/rejected`
   + `$aws/things/mySimulatedThing/shadow/name/simShadow1/update/accepted`
   + `$aws/things/mySimulatedThing/shadow/name/simShadow1/update/rejected`
   + `$aws/things/mySimulatedThing/shadow/name/simShadow1/update/delta`
   + `$aws/things/mySimulatedThing/shadow/name/simShadow1/update/documents`

   此時，您的模擬裝置已準備好接收由 AWS IoT發佈的主題。

**如要訂閱 **MQTT 用戶端**中的 MQTT 主題**  
在裝置自行初始化並訂閱回應主題之後，它會查詢所支援的影子。這個模擬支援只有一個影子，該影子命名為 *simShadow1*，支援命名為 *mySimulatedThing* 的物件物件。

**從 **MQTT 用戶端**取得目前影子狀態**

1. 於 **MQTT 用戶端** 中，選擇 **Publish to a topic (發佈至主題)**。

1. 在 **Publish** (發佈) 之下，輸入下列主題，然後刪除下方主題訊息內文視窗的任何內容，您可在其中輸入要取得的主題。接著，您可選擇 **Publish to topic** (發佈至主題)，發佈請求。`$aws/things/mySimulatedThing/shadow/name/simShadow1/get`。

   若您尚未建立命名的影子 `simShadow1`， 您收到了 `$aws/things/mySimulatedThing/shadow/name/simShadow1/get/rejected` 主題中的一則訊息，且 `code` 為 `404`，(如此範例中所示)，則系統尚未建立該影子，因此我們接下來將其建立。

   ```
   {
     "code": 404,
     "message": "No shadow exists with name: 'simShadow1'"
   }
   ```

**建立具有裝置目前狀態的影子**

1. 在 **MQTT 用戶端**中，選擇**Publish to a topic** (發佈到主題)，然後輸入此主題：

   ```
   $aws/things/mySimulatedThing/shadow/name/simShadow1/update
   ```

1. 在您輸入主題的下方訊息內文視窗中，輸入此影子文件以顯示裝置正在報告其 ID 及其目前顏色的 RGB 值。選擇 **Publish** (發佈)，發佈請求。

   ```
   {
     "state": {
       "reported": {
         "ID": "SmartLamp21",
         "ColorRGB": [
           128,
           128,
           128
         ]
       }
     },
     "clientToken": "426bfd96-e720-46d3-95cd-014e3ef12bb6"
   }
   ```

若您在主題中收到一則訊息：
+ `$aws/things/mySimulatedThing/shadow/name/simShadow1/update/accepted`：這表示已建立影子，而訊息內文包含目前的影子文件。
+ `$aws/things/mySimulatedThing/shadow/name/simShadow1/update/rejected`：檢閱訊息內文中的錯誤。
+ `$aws/things/mySimulatedThing/shadow/name/simShadow1/get/accepted`：影子已存在，且訊息內文具有目前的影子狀態，例如在此範例中。有了這個，您可以設置您的裝置或確認它符合影子狀態。

  ```
  {
    "state": {
      "reported": {
        "ID": "SmartLamp21",
        "ColorRGB": [
          128,
          128,
          128
        ]
      }
    },
    "metadata": {
      "reported": {
        "ID": {
          "timestamp": 1591140517
        },
        "ColorRGB": [
          {
            "timestamp": 1591140517
          },
          {
            "timestamp": 1591140517
          },
          {
            "timestamp": 1591140517
          }
        ]
      }
    },
    "version": 3,
    "timestamp": 1591140517,
    "clientToken": "426bfd96-e720-46d3-95cd-014e3ef12bb6"
  }
  ```

## 從應用程式傳送更新
<a name="using-device-shadows-app-update"></a>

本節使用 AWS CLI 來示範應用程式如何與影子互動。

**使用 取得影子的目前狀態 AWS CLI**  
在命令列輸入此命令：

```
aws iot-data get-thing-shadow --thing-name mySimulatedThing --shadow-name simShadow1 /dev/stdout
```

在 Windows 平台上，您可使用 `con`，而非 `/dev/stdout`。

```
aws iot-data get-thing-shadow --thing-name mySimulatedThing --shadow-name simShadow1 con
```

因為影子已存在，而且已由裝置初始化以反映其目前狀態，所以它會傳回下列的影子文件。

```
{
  "state": {
    "reported": {
      "ID": "SmartLamp21",
      "ColorRGB": [
        128,
        128,
        128
      ]
    }
  },
  "metadata": {
    "reported": {
      "ID": {
        "timestamp": 1591140517
      },
      "ColorRGB": [
        {
          "timestamp": 1591140517
        },
        {
          "timestamp": 1591140517
        },
        {
          "timestamp": 1591140517
        }
      ]
    }
  },
  "version": 3,
  "timestamp": 1591141111
}
```

應用程式可以使用此回應來初始化對其裝置狀態的表示法。

如果應用程式更新狀態，例如當使用者將智慧型燈泡的顏色變更為黃色時，應用程式會傳送 **update-thing-shadow** 命令。此命令會對應 `UpdateThingShadow` REST API。

**從應用程式更新影子**  
在命令列輸入此命令：

------
#### [ AWS CLI v2.x ]

```
aws iot-data update-thing-shadow --thing-name mySimulatedThing --shadow-name simShadow1 \
    --cli-binary-format raw-in-base64-out \
    --payload '{"state":{"desired":{"ColorRGB":[255,255,0]}},"clientToken":"21b21b21-bfd2-4279-8c65-e2f697ff4fab"}' /dev/stdout
```

------
#### [ AWS CLI v1.x ]

```
aws iot-data update-thing-shadow --thing-name mySimulatedThing --shadow-name simShadow1 \
    --payload '{"state":{"desired":{"ColorRGB":[255,255,0]}},"clientToken":"21b21b21-bfd2-4279-8c65-e2f697ff4fab"}' /dev/stdout
```

------

如果成功，這個命令應該會傳回下列影子文件。

```
{
  "state": {
    "desired": {
      "ColorRGB": [
        255,
        255,
        0
      ]
    }
  },
  "metadata": {
    "desired": {
      "ColorRGB": [
        {
          "timestamp": 1591141596
        },
        {
          "timestamp": 1591141596
        },
        {
          "timestamp": 1591141596
        }
      ]
    }
  },
  "version": 4,
  "timestamp": 1591141596,
  "clientToken": "21b21b21-bfd2-4279-8c65-e2f697ff4fab"
}
```

## 回應裝置中的更新
<a name="using-device-shadows-device-update"></a>

返回 AWS 主控台中的 **MQTT 用戶端**時，您應該會看到 AWS IoT 發佈的訊息，以反映上一節中發出的更新命令。

**在 **MQTT 用戶端**中檢視更新訊息**  
於 **MQTT 用戶端**中，在 **Subscriptions** (訂閱) 欄中選擇 **\$1aws/things/mySimulatedThing/shadow/name/simShadow1/update/delta**。如果主題名稱被截斷，您可以暫停以查看完整主題。在本主題的主題記錄中，您應會看到類似此訊息的 `/delta` 訊息。

```
{
  "version": 4,
  "timestamp": 1591141596,
  "state": {
    "ColorRGB": [
      255,
      255,
      0
    ]
  },
  "metadata": {
    "ColorRGB": [
      {
        "timestamp": 1591141596
      },
      {
        "timestamp": 1591141596
      },
      {
        "timestamp": 1591141596
      }
    ]
  },
  "clientToken": "21b21b21-bfd2-4279-8c65-e2f697ff4fab"
}
```

您的裝置會處理此訊息的內容，以將裝置狀態設定為符合訊息中的 `desired` 狀態。

裝置更新狀態以符合訊息中的`desired`狀態後，必須透過 AWS IoT 發佈更新訊息將新的報告狀態傳回 。此程序會在 **MQTT 用戶端**中模擬這個部分。

**從裝置更新影子**

1. 於 **MQTT 用戶端** 中，選擇 **Publish to a topic (發佈至主題)**。

1. 在訊息內文視窗的訊息內文視窗上方的主題欄位中，輸入影子的主題，接著輸入 `/update` 動作：`$aws/things/mySimulatedThing/shadow/name/simShadow1/update`，並在訊息內文中輸入此更新的影子文件，其中說明裝置的目前狀態。選擇 **Publish** (發佈)，發佈更新的裝置狀態。

   ```
   {
     "state": {
       "reported": {
         "ColorRGB": [255,255,0]
         }
     },
     "clientToken": "a4dc2227-9213-4c6a-a6a5-053304f60258"
   }
   ```

   如果成功收到訊息 AWS IoT，您應該會在 **MQTT 用戶端**的 **\$1aws/things/mySimulatedThing/shadow/name/simShadow1/update/accepted** 訊息日誌中看到新的回應，其中包含影子的目前狀態，例如此範例。

   ```
   {
     "state": {
       "reported": {
         "ColorRGB": [
           255,
           255,
           0
         ]
       }
     },
     "metadata": {
       "reported": {
         "ColorRGB": [
           {
             "timestamp": 1591142747
           },
           {
             "timestamp": 1591142747
           },
           {
             "timestamp": 1591142747
           }
         ]
       }
     },
     "version": 5,
     "timestamp": 1591142747,
     "clientToken": "a4dc2227-9213-4c6a-a6a5-053304f60258"
   }
   ```

成功更新回報的裝置狀態，也會導致 AWS IoT 將訊息中影子狀態的完整描述傳送至`update/documents`主題，例如由裝置在上述程序中執行的影子更新所產生的此訊息內文。

```
{
  "previous": {
    "state": {
      "desired": {
        "ColorRGB": [
          255,
          255,
          0
        ]
      },
      "reported": {
        "ID": "SmartLamp21",
        "ColorRGB": [
          128,
          128,
          128
        ]
      }
    },
    "metadata": {
      "desired": {
        "ColorRGB": [
          {
            "timestamp": 1591141596
          },
          {
            "timestamp": 1591141596
          },
          {
            "timestamp": 1591141596
          }
        ]
      },
      "reported": {
        "ID": {
          "timestamp": 1591140517
        },
        "ColorRGB": [
          {
            "timestamp": 1591140517
          },
          {
            "timestamp": 1591140517
          },
          {
            "timestamp": 1591140517
          }
        ]
      }
    },
    "version": 4
  },
  "current": {
    "state": {
      "desired": {
        "ColorRGB": [
          255,
          255,
          0
        ]
      },
      "reported": {
        "ID": "SmartLamp21",
        "ColorRGB": [
          255,
          255,
          0
        ]
      }
    },
    "metadata": {
      "desired": {
        "ColorRGB": [
          {
            "timestamp": 1591141596
          },
          {
            "timestamp": 1591141596
          },
          {
            "timestamp": 1591141596
          }
        ]
      },
      "reported": {
        "ID": {
          "timestamp": 1591140517
        },
        "ColorRGB": [
          {
            "timestamp": 1591142747
          },
          {
            "timestamp": 1591142747
          },
          {
            "timestamp": 1591142747
          }
        ]
      }
    },
    "version": 5
  },
  "timestamp": 1591142747,
  "clientToken": "a4dc2227-9213-4c6a-a6a5-053304f60258"
}
```

## 觀察應用程式中的更新
<a name="using-device-shadows-view-result"></a>

應用程式現在可以查詢裝置報告的目前狀態的影子。

**使用 取得影子的目前狀態 AWS CLI**

1. 在命令列輸入此命令：

   ```
   aws iot-data get-thing-shadow --thing-name mySimulatedThing --shadow-name simShadow1 /dev/stdout
   ```

   在 Windows 平台上，您可使用 `con`，而非 `/dev/stdout`。

   ```
   aws iot-data get-thing-shadow --thing-name mySimulatedThing --shadow-name simShadow1 con
   ```

1. 因為裝置剛剛更新影子，以反映其目前的狀態，所以應該傳回下列影子文件。

   ```
   {
     "state": {
       "desired": {
         "ColorRGB": [
           255,
           255,
           0
         ]
       },
       "reported": {
         "ID": "SmartLamp21",
         "ColorRGB": [
           255,
           255,
           0
         ]
       }
     },
     "metadata": {
       "desired": {
         "ColorRGB": [
           {
             "timestamp": 1591141596
           },
           {
             "timestamp": 1591141596
           },
           {
             "timestamp": 1591141596
           }
         ]
       },
       "reported": {
         "ID": {
           "timestamp": 1591140517
         },
         "ColorRGB": [
           {
             "timestamp": 1591142747
           },
           {
             "timestamp": 1591142747
           },
           {
             "timestamp": 1591142747
           }
         ]
       }
     },
     "version": 5,
     "timestamp": 1591143269
   }
   ```

## 超越模擬
<a name="using-device-shadows-next-steps"></a>

試驗 AWS CLI (代表應用程式) 與主控台 (代表裝置) 之間的互動，以建立 IoT 解決方案的模型。

# 與影子互動
<a name="device-shadow-data-flow"></a>

本主題說明與 AWS IoT 提供使用影子的三種方法中的每一種相關聯的訊息。這些方法包括下列各項：

`UPDATE`  <a name="update"></a>
建立一個影子，如果它不存在，或者使用訊息正文中提供的狀態訊息更新現有影子的內容。 AWS IoT 會記錄每次更新的時間戳記，以指出上次更新狀態的時間。當影子的狀態變更時， 會 AWS IoT 傳送訊息`/delta`給所有 MQTT 訂閱者，並顯示 `desired`與 `reported` 狀態之間的差異。接收 `/delta` 訊息的裝置或應用程式會根據差異執行動作。例如，裝置可以更新其狀態至所需的狀態，或者應用程式可以更新 UI 以反映裝置狀態的改變。

`GET`  <a name="get"></a>
擷取包含影子完整狀態的目前影子文件，包括中繼資料。

`DELETE`  <a name="delete"></a>
刪除裝置影子及其內容。  
您無法還原已刪除的裝置影子文件，但可以使用已刪除的裝置影子文件的名稱建立新的裝置影子。如果您建立的裝置影子文件名稱與過去 48 小時內刪除的文件名稱相同，則新裝置影子文件的版本號碼會跟隨已刪除影子的版本號碼。如果裝置影子文件已刪除超過 48 小時，則使用相同名稱的新裝置影子文件的版本號碼將會是 0。

## 通訊協定支援
<a name="protocol-support"></a>

AWS IoT 透過 HTTPS 通訊協定支援 [MQTT](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html) 和 REST API 來與影子互動。 為 MQTT 發佈和訂閱動作 AWS IoT 提供一組預留請求和回應主題。裝置和應用程式應先訂閱回應主題，再發佈至請求主題，以取得 AWS IoT 如何處理請求的資訊。如需詳細資訊，請參閱[Device Shadow MQTT 主題](device-shadow-mqtt.md)及[Device Shadow REST API](device-shadow-rest-api.md)。

## 請求和報告狀態
<a name="shadow-reporting-state"></a>

使用 AWS IoT 和影子設計 IoT 解決方案時，您應該判斷將請求變更的應用程式或裝置，以及將實作變更的應用程式或裝置。一般而言，裝置會實作並報告變更回影子，而應用程式和服務會回應並請求影子中的變更。您的解決方案可能不同，但本主題中的範例假設用戶端應用程式或服務請求變更影子中，而裝置會執行變更，並將它們回報給影子。

## 更新影子
<a name="update-device-shadow"></a>

您的應用程式或服務可以使用 [UpdateThingShadow](device-shadow-rest-api.md#API_UpdateThingShadow) API 或發佈至 [/update](device-shadow-mqtt.md#update-pub-sub-topic) 主題來更新影子的狀態。更新只會影響請求中所指定的欄位。

### 當用戶端請求狀態變更時更新影子
<a name="update-pub-sub-topic-client"></a>

**當用戶端使用 MQTT 通訊協定請求影子中的狀態變更**

1. 用戶端應具有目前的影子文件，以便識別要變更的屬性。請參閱 /get 動作，了解如何取得目前的影子文件。

1. 用戶端會訂閱下列 MQTT 主題：
   + `$aws/things/thingName/shadow/name/shadowName/update/accepted`
   + `$aws/things/thingName/shadow/name/shadowName/update/rejected`
   + `$aws/things/thingName/shadow/name/shadowName/update/delta`
   + `$aws/things/thingName/shadow/name/shadowName/update/documents`

1. 用戶端會發佈 `$aws/things/thingName/shadow/name/shadowName/update` 請求主題，其中包含所需的影子狀態的狀態文件。只有要變更的屬性需要包含在文件中。這是具有所需狀態的文件的範例。

   ```
   {
     "state": {
       "desired": {
         "color": {
           "r": 10
         },
         "engine": "ON"
       }
     }
   }
   ```

1. 如果更新請求有效， 會在影子中 AWS IoT 更新所需的狀態，並在這些主題上發佈訊息：
   + `$aws/things/thingName/shadow/name/shadowName/update/accepted`
   + `$aws/things/thingName/shadow/name/shadowName/update/delta`

   `/update/accepted` 訊息包含 [/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted) 影子文件，而 `/update/delta` 訊息包含 [/delta response state document](device-shadow-document.md#device-shadow-example-response-json-delta) 影子文件。

1. 如果更新請求無效， 會使用描述錯誤的[錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)影子文件 AWS IoT 發佈具有 `$aws/things/thingName/shadow/name/shadowName/update/rejected`主題的訊息。

**當用戶端請求在影子中使用 API 的狀態變更**

1. 用戶端呼叫 `UpdateThingShadow` API，包含作為其訊息內文的 [請求狀態文件](device-shadow-document.md#device-shadow-example-request-json) 狀態文件。

1. 如果請求有效， 會 AWS IoT 傳回 HTTP 成功回應碼和[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文件作為其回應訊息內文。

   AWS IoT 也會發佈 MQTT 訊息至 `$aws/things/thingName/shadow/name/shadowName/update/delta`主題，其中包含訂閱該訊息的任何裝置或用戶端的[/delta response state document](device-shadow-document.md#device-shadow-example-response-json-delta)影子文件。

1. 如果請求無效， 會 AWS IoT 傳回 HTTP 錯誤回應代碼 [錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)作為其回應訊息內文。

當裝置收到有關 `/update/delta` 主題的 `/desired` 狀態時，它會在裝置中進行所需的變更。然後，它會將訊息傳送至 `/update` 主題，向影子報告其目前狀態。

### 當裝置回報其目前狀態時更新影子
<a name="update-pub-sub-topic-device"></a>

**當裝置使用 MQTT 通訊協定向影子報告其目前的狀態**

1. 裝置應該在更新影子之前訂閱這些 MQTT 主題：
   + `$aws/things/thingName/shadow/name/shadowName/update/accepted`
   + `$aws/things/thingName/shadow/name/shadowName/update/rejected`
   + `$aws/things/thingName/shadow/name/shadowName/update/delta`
   + `$aws/things/thingName/shadow/name/shadowName/update/documents`

1. 裝置會將訊息發佈至報告目前狀態的 `$aws/things/thingName/shadow/name/shadowName/update` 主題，例如在此範例中，以報告其目前狀態。

   ```
   {
       "state": {
           "reported" : {
               "color" : { "r" : 10 },
               "engine" : "ON"
           }
       }
   }
   ```

1. 如果 AWS IoT 接受更新，它會發佈訊息到具有[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文件`$aws/things/thingName/shadow/name/shadowName/update/accepted`的主題。

1. 如果更新請求無效， 會使用描述錯誤的[錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)影子文件 AWS IoT 發佈具有 `$aws/things/thingName/shadow/name/shadowName/update/rejected`主題的訊息。

**當裝置藉由使用 API 向影子報告其目前的狀態**

1. 裝置會呼叫 `UpdateThingShadow` API，包含 [請求狀態文件](device-shadow-document.md#device-shadow-example-request-json) 狀態文件作為其訊息正文。

1. 如果請求有效， 會 AWS IoT 更新影子，並傳回 HTTP 成功回應碼，其中包含影[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)子文件作為其回應訊息內文。

   AWS IoT 也會發佈 MQTT 訊息至 `$aws/things/thingName/shadow/name/shadowName/update/delta`主題，其中包含訂閱該訊息的任何裝置或用戶端的[/delta response state document](device-shadow-document.md#device-shadow-example-response-json-delta)影子文件。

1. 如果請求無效， 會 AWS IoT 傳回 HTTP 錯誤回應代碼 [錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)作為其回應訊息內文。

### 樂觀鎖定
<a name="optimistic-locking"></a>

您可以使用狀態文件版本，確認您正在更新的裝置影子文件為最新版本。當您為更新請求提供版本，若狀態文件的目前版本與提供的版本不符，則服務會拒絕請求並顯示 HTTP 409 衝突回應代碼。衝突回應代碼也會出現在任何修改 `ThingShadow` 的 API 上，包括 `DeleteThingShadow`。

例如：

初始文件：

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

更新：(版本不符合；系統將拒絕請求)

```
{
  "state": {
    "desired": {
      "colors": [
        "BLUE"
      ]
    }
  },
  "version": 9
}
```

結果：

```
{
  "code": 409,
  "message": "Version conflict",
  "clientToken": "426bfd96-e720-46d3-95cd-014e3ef12bb6"
}
```

更新：(版本符合；將接受請求)

```
{
  "state": {
    "desired": {
      "colors": [
        "BLUE"
      ]
    }
  },
  "version": 10
}
```

最終狀態：

```
{
  "state": {
    "desired": {
      "colors": [
        "BLUE"
      ]
    }
  },
  "version": 11
}
```

## 擷取影子文件
<a name="retrieving-device-shadow"></a>

您可以使用 [GetThingShadow](device-shadow-rest-api.md#API_GetThingShadow) API 或訂閱並發行至 [/get](device-shadow-mqtt.md#get-pub-sub-topic) 主題來擷取影子文件。這會擷取完整的影子文件，包括 `desired` 和 `reported` 狀態之間的任何差異。無論裝置或用戶端正在提出請求，此工作的程序都是相同的。

**使用 MQTT 通訊協定擷取影子文件**

1. 裝置或用戶端應該在更新影子之前訂閱這些 MQTT 主題：
   + `$aws/things/thingName/shadow/name/shadowName/get/accepted`
   + `$aws/things/thingName/shadow/name/shadowName/get/rejected`

1. 裝置或用戶端會將訊息發佈至具有空白訊息內文的 `$aws/things/thingName/shadow/name/shadowName/get` 主題。

1. 如果請求成功， 會在訊息內文[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)中使用 AWS IoT 發佈訊息至`$aws/things/thingName/shadow/name/shadowName/get/accepted`主題。

1. 如果請求無效， 會將訊息 AWS IoT 發佈至訊息內文[錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)中具有 的 `$aws/things/thingName/shadow/name/shadowName/get/rejected`主題。

**使用 REST API 擷取影子文件**

1. 裝置或用戶端會使用空的訊息內文呼叫 `GetThingShadow` API。

1. 如果請求有效， 會 AWS IoT 傳回 HTTP 成功回應程式碼，並將[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文件作為回應訊息內文。

1. 如果請求無效， 會 AWS IoT 傳回 HTTP 錯誤回應代碼 [錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)作為其回應訊息內文。

## 刪除影子資料
<a name="deleting-thing-data"></a>

有兩種方法可以刪除影子資料：您可以刪除影子文件中的特定屬性，也可以完全刪除影子。
+ 若要從影子中刪除特定屬性，請更新影子；不過，請將您要刪除的屬性值設定為 `null`。值為 `null` 的欄位會從影子文件中移除。
+ 若要刪除整個影子，請使用 [DeleteThingShadow](device-shadow-rest-api.md#API_DeleteThingShadow) API 或發佈至 [/delete](device-shadow-mqtt.md#delete-pub-sub-topic) 主題。

**注意**  
刪除影子不會一次將其版本編號重設為零。其將於 48 小時後重設為零。

### 從影子文件中刪除屬性
<a name="deleting-shadow-property"></a>

**使用 MQTT 通訊協定從影子刪除屬性**

1. 裝置或用戶端應該有目前的影子文件，以便識別要變更的屬性。如需如何取得目前影子文件的詳細資訊，請參閱 [擷取影子文件](#retrieving-device-shadow)。

1. 裝置或用戶端會訂閱下列 MQTT 主題：
   + `$aws/things/thingName/shadow/name/shadowName/update/accepted`
   + `$aws/things/thingName/shadow/name/shadowName/update/rejected`

1. 裝置或用戶端會發佈 `$aws/things/thingName/shadow/name/shadowName/update` 請求主題，其中包含狀態文件，該文件會將 `null` 值指派給要刪除的影子屬性。只有要變更的屬性需要包含在文件中。這是刪除 `engine` 屬性的文件範例。

   ```
   {
     "state": {
       "desired": {
         "engine": null
       }
     }
   }
   ```

1. 如果更新請求有效， 會 AWS IoT 刪除影子中指定的屬性，並在訊息內文中發佈具有影[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)子文件的 `$aws/things/thingName/shadow/name/shadowName/update/accepted`主題訊息。

1. 如果更新請求無效， 會使用描述錯誤的[錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)影子文件 AWS IoT 發佈具有 `$aws/things/thingName/shadow/name/shadowName/update/rejected`主題的訊息。

**使用 REST API 從影子刪除屬性**

1. 裝置或用戶端會呼叫 `UpdateThingShadow` API，其具有將 `null` 值指派給要刪除之影子屬性的 [請求狀態文件](device-shadow-document.md#device-shadow-example-request-json)。只包含您要在文件中刪除的屬性。這是刪除 `engine` 屬性的文件範例。

   ```
   {
     "state": {
       "desired": {
         "engine": null
       }
     }
   }
   ```

1. 如果請求有效， 會 AWS IoT 傳回 HTTP 成功回應碼和[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文件作為其回應訊息內文。

1. 如果請求無效， 會 AWS IoT 傳回 HTTP 錯誤回應代碼 [錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)作為其回應訊息內文。

### 刪除影子
<a name="deleting-device-shadow"></a>

以下是刪除裝置影子時的一些考量事項。
+ 將裝置的影子狀態設定為 `null` 不會刪除影子。影子版本將在下一次更新時遞增。
+ 刪除裝置的影子並不會刪除物件物件。刪除物件物件也不會刪除對應的裝置影子。
+ 刪除影子不會一次將其版本編號重設為零。其將於 48 小時後重設為零。

**使用 MQTT 通訊協定刪除影子**

1. 裝置或用戶端會訂閱下列 MQTT 主題：
   + `$aws/things/thingName/shadow/name/shadowName/delete/accepted`
   + `$aws/things/thingName/shadow/name/shadowName/delete/rejected`

1. 裝置或用戶端會發佈具有空白訊息緩衝區的 `$aws/things/thingName/shadow/name/shadowName/delete`。

1. 如果刪除請求有效， 會 AWS IoT 刪除影子，並在訊息內文中發佈具有`$aws/things/thingName/shadow/name/shadowName/delete/accepted`主題和縮寫影[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)子文件的訊息。以下是接受的刪除訊息範例：

   ```
   {
     "version": 4,
     "timestamp": 1591057529
   }
   ```

1. 如果更新請求無效， 會使用描述錯誤的[錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)影子文件 AWS IoT 發佈具有 `$aws/things/thingName/shadow/name/shadowName/delete/rejected`主題的訊息。

**使用 REST API 刪除影子**

1. 裝置或用戶端會使用空的訊息緩衝區呼叫 `DeleteThingShadow` API。

1. 如果請求有效， 會在訊息內文中 AWS IoT 傳回 HTTP 成功回應碼和 [/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)以及縮寫[/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)陰影文件。以下是接受的刪除訊息範例：

   ```
   {
     "version": 4,
     "timestamp": 1591057529
   }
   ```

1. 如果請求無效， 會 AWS IoT 傳回 HTTP 錯誤回應代碼 [錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)作為其回應訊息內文。

# Device Shadow REST API
<a name="device-shadow-rest-api"></a>

影子會公開下列 URI 以更新狀態資訊：

```
https://account-specific-prefix-ats.iot.region.amazonaws.com/things/thingName/shadow
```

端點專屬於您的 AWS 帳戶。如要尋找您的端點，您可以：
+ 使用來自 AWS CLI的 [describe-endpoint](https://docs.aws.amazon.com/cli/latest/reference/iot/describe-endpoint.html) 命令。
+ 使用 AWS IoT 主控台設定。於 **Settings** (設定) 中，端點會列於 **Custom endpoint** (自訂端點) 之下
+ 使用 AWS IoT 主控台物件詳細資訊頁面。在主控台中：

  1. 開啟 **Manage** (管理)，在 **Manage** (管理) 之下，選擇 **Things** (物件)。

  1. 於物件清單中，選擇要取得端點 URI 的物件。

  1. 選擇 **Device Shadows** 索引標籤，選擇您的影子。您可在 **Device Shadow 詳細資訊**頁面的 **Device Shadow URL** 區段中檢視端點 URI。

端點格式如下：

```
identifier.iot.region.amazonaws.com
```

影子 REST API 會遵循相同的 HTTPS 通訊協定/連接埠映射，如 [裝置通訊協定](protocols.md) 中所述。

**注意**  
若要使用 API，您必須使用 `iotdevicegateway` 作為身分驗證的服務名稱。如需詳細資訊，請參閱 [IoTDataPlane](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-iot-data-plane/classes/iotdataplane.html)。

**Topics**
+ [GetThingShadow](#API_GetThingShadow)
+ [UpdateThingShadow](#API_UpdateThingShadow)
+ [DeleteThingShadow](#API_DeleteThingShadow)
+ [ListNamedShadowsForThing](#API_ListNamedShadowsForThing)

您也可以透過提供 `name=shadowName` 作為 API 的查詢參數的一部分，使用 API 建立已命名的影子。

## GetThingShadow
<a name="API_GetThingShadow"></a>

取得特定物件的影子。

回應狀態文件包括 `desired` 和 `reported` 狀態之間的差量。

**請求**  
此請求包括標準 HTTP 標頭加上以下 URI：

```
HTTP GET https://endpoint/things/thingName/shadow?name=shadowName
Request body: (none)
```

未命名 (典型) 影子不需要 `name` 查詢參數。

**回應**  
成功時，回應會包括標準 HTTP 標頭以及下列程式碼與本文：

```
HTTP 200
Response Body: response state document
```

如需詳細資訊，請參閱[回應狀態文件範例](device-shadow-document.md#device-shadow-example-response-json)。

**Authorization**  
擷取影子需要一項允許呼叫者可執行 `iot:GetThingShadow` 動作的政策。Device Shadow 服務接受兩種形式的身分驗證：使用 IAM 憑證的 Signature 第 4 版，或使用用戶端憑證的 TLS 交互身分驗證。

下列為允許呼叫者擷取裝置影子的政策範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iot:GetThingShadow",
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:thing/thing"
            ]
        }
    ]
}
```

## UpdateThingShadow
<a name="API_UpdateThingShadow"></a>

更新指定之物件的影子。

更新僅會影響請求狀態文件中所指定的欄位。值為 `null` 的任何欄位，皆會從裝置影子中移除。

**請求**  
此請求包含標準 HTTP 標頭和內文加上以下 URI 與本文：

```
HTTP POST https://endpoint/things/thingName/shadow?name=shadowName
Request body: request state document
```

未命名 (典型) 影子不需要 `name` 查詢參數。

如需詳細資訊，請參閱[請求狀態文件範例](device-shadow-document.md#device-shadow-example-request-json)。

**回應**  
成功時，回應會包括標準 HTTP 標頭以及下列程式碼與本文：

```
HTTP 200
Response body: response state document
```

如需詳細資訊，請參閱[回應狀態文件範例](device-shadow-document.md#device-shadow-example-response-json)。

**Authorization**  
更新影子需要一項允許呼叫者執行 `iot:UpdateThingShadow` 動作的政策。Device Shadow 服務接受兩種形式的身分驗證：使用 IAM 憑證的 Signature 第 4 版，或使用用戶端憑證的 TLS 交互身分驗證。

下列為允許呼叫者更新裝置影子的政策範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iot:UpdateThingShadow",
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:thing/thing"
            ]
        }
    ]
}
```

## DeleteThingShadow
<a name="API_DeleteThingShadow"></a>

刪除特定物件的影子。

**請求**  
此請求包括標準 HTTP 標頭加上以下 URI：

```
HTTP DELETE https://endpoint/things/thingName/shadow?name=shadowName
Request body: (none)
```

未命名 (典型) 影子不需要 `name` 查詢參數。

**回應**  
成功時，回應會包括標準 HTTP 標頭以及下列程式碼與本文：

```
HTTP 200
Response body: Empty response state document
```

請注意，刪除影子並不會將其版本號碼重設為 0。

**Authorization**  
刪除裝置影子需要一項允許呼叫者執行 `iot:DeleteThingShadow` 動作的政策。Device Shadow 服務接受兩種形式的身分驗證：使用 IAM 憑證的 Signature 第 4 版，或使用用戶端憑證的 TLS 交互身分驗證。

下列為允許呼叫者刪除裝置影子的政策範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iot:DeleteThingShadow",
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:thing/thing"
            ]
        }
    ]
}
```

## ListNamedShadowsForThing
<a name="API_ListNamedShadowsForThing"></a>

列出指定物件的影子。

**請求**  
此請求包括標準 HTTP 標頭加上以下 URI：

```
HTTP GET /api/things/shadow/ListNamedShadowsForThing/thingName?nextToken=nextToken&pageSize=pageSize
Request body: (none)
```

nextToken  
用來擷取下一組結果的符記。  
此值會在分頁結果上傳回，並在傳回下一頁的呼叫中使用。

pageSize  
影子名稱在每個呼叫中傳回的數目。另請參閱`nextToken`。

thingName  
要列出命名影子的物件的名稱。

**回應**  
成功時，回應會包括標準 HTTP 標頭以及下列回應程式碼與 [影子名稱清單回應文件](device-shadow-document.md#device-shadow-list-json)。

**注意**  
未命名 (傳統) 影子不會出現在此清單中。如果您只有一個經典的影子，或者您指定的 `thingName` 不存在，則回應將是一個空清單。

```
HTTP 200
Response body: Shadow name list document
```

**Authorization**  
列出裝置影子需要一項允許呼叫者執行 `iot:ListNamedShadowsForThing` 動作的政策。Device Shadow 服務接受兩種形式的身分驗證：使用 IAM 憑證的 Signature 第 4 版，或使用用戶端憑證的 TLS 交互身分驗證。

以下為允許呼叫者更新物件已命名影子的範例政策：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iot:ListNamedShadowsForThing",
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:thing/thing"
            ]
        }
    ]
}
```

# Device Shadow MQTT 主題
<a name="device-shadow-mqtt"></a>

Device Shadow 服務會使用預留的 MQTT 主題，讓應用程式與裝置可取得、更新或刪除裝置 (影子) 的狀態資訊。

在影子主題上發佈和訂閱需要主題為基礎的授權。 AWS IoT 有權新增主題到現有的主題結構。因此，我們建議您避免使用萬用字元訂閱影子主題。例如，避免像 一樣訂閱主題篩選條件，`$aws/things/thingName/shadow/#`因為當 AWS IoT 引入新的影子主題時，符合此主題篩選條件的主題數量可能會增加。關於發佈至這些主題的訊息範例，請參閱 [與影子互動](device-shadow-data-flow.md)。

影子可以為已命名或未命名 (典型)。各影子所使用的主題只有在主題字首中有所不同。此表格會顯示每種影子類型所使用的主題字首。


| *ShadowTopicPrefix* 值 | 影子類型 | 
| --- | --- | 
| \$1aws/things/thingName/shadow | 未命名 (經典) 影子 | 
| \$1aws/things/thingName/shadow/name/shadowName | 已命名影子 | 

若要建立完整的主題，請在 `ShadowTopicPrefix` 選取您要參照的影子類型、用對應值取代 `thingName` 及 `shadowName` (如適用) 的影子類型，然後將該類型附加至主題 stub，如下節所示。

下列為用來與影子互動的 MQTT 主題。

**Topics**
+ [/get](#get-pub-sub-topic)
+ [/get/accepted](#get-accepted-pub-sub-topic)
+ [/get/rejected](#get-rejected-pub-sub-topic)
+ [/update](#update-pub-sub-topic)
+ [/update/delta](#update-delta-pub-sub-topic)
+ [/update/accepted](#update-accepted-pub-sub-topic)
+ [/update/documents](#update-documents-pub-sub-topic)
+ [/update/rejected](#update-rejected-pub-sub-topic)
+ [/delete](#delete-pub-sub-topic)
+ [/delete/accepted](#delete-accepted-pub-sub-topic)
+ [/delete/rejected](#delete-rejected-pub-sub-topic)

## /get
<a name="get-pub-sub-topic"></a>

發佈空白訊息至此主題可取得裝置的影子：

```
ShadowTopicPrefix/get
```

AWS IoT 透過發佈至 [/get/accepted](#get-accepted-pub-sub-topic)或 來回應 [/get/rejected](#get-rejected-pub-sub-topic)。

### 範例 政策
<a name="get-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Publish"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/get"
            ]
        }
    ]
}
```

## /get/accepted
<a name="get-accepted-pub-sub-topic"></a>

AWS IoT 傳回裝置的影子時， 會將回應影子文件發佈至此主題：

```
ShadowTopicPrefix/get/accepted
```

如需詳細資訊，請參閱[回應狀態文件](device-shadow-document.md#device-shadow-example-response-json)。

### 範例 政策
<a name="get-accepted-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingName/shadow/get/accepted"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/get/accepted"
            ]
        }
    ]
}
```

## /get/rejected
<a name="get-rejected-pub-sub-topic"></a>

AWS IoT 當無法傳回裝置的影子時， 會將錯誤回應文件發佈至此主題：

```
ShadowTopicPrefix/get/rejected
```

如需詳細資訊，請參閱[錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)。

### 範例 政策
<a name="get-rejected-policy"></a>

以下為所需政策的範例：

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Subscribe"
      ],
      "Resource": [
        "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingName/shadow/get/rejected"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/get/rejected"
      ]
    }
  ]
}
```

## /update
<a name="update-pub-sub-topic"></a>

對此主題發佈要求狀態文件，以更新裝置的影子：

```
ShadowTopicPrefix/update
```

訊息內文包含[部分請求狀態文件](device-shadow-document.md#device-shadow-example-request-json)。

嘗試更新裝置狀態的用戶端會發送一個 JSON 請求狀態文件，其 `desired` 屬性如下：

```
{
  "state": {
    "desired": {
      "color": "red",
      "power": "on"
    }
  }
}
```

更新其影子的裝置將發送一個 JSON 請求狀態文件與該 `reported` 屬性，例如：

```
{
  "state": {
    "reported": {
      "color": "red",
      "power": "on"
    }
  }
}
```

AWS IoT 透過發佈至 [/update/accepted](#update-accepted-pub-sub-topic)或 來回應 [/update/rejected](#update-rejected-pub-sub-topic)。

### 範例 政策
<a name="update-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Publish"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/update"
            ]
        }
    ]
}
```

## /update/delta
<a name="update-delta-pub-sub-topic"></a>

AWS IoT 當回應狀態文件接受裝置影子的變更，且回應狀態文件包含 `desired`和 `reported` 狀態的不同值時，會將回應狀態文件發佈至此主題：

```
ShadowTopicPrefix/update/delta
```

訊息緩衝區包含 [/delta response state document](device-shadow-document.md#device-shadow-example-response-json-delta)。

### 訊息內文詳細資料
<a name="update-delta-rules"></a>
+ 發佈於 `update/delta` 的訊息，僅包括 `desired` 和 `reported` 部分之間不同的所需屬性。這些屬性全都會包含在其中，無論這些屬性是否原本就包含於目前的更新訊息中或是已經存放於 AWS IoT。在 `desired` 和 `reported` 部分之間沒有差異的屬性則不會包含在內。
+ 如果某個屬性出現在 `reported` 部分，但在 `desired` 部分卻沒有，則此屬性不會包含在內。
+ 如果某個屬性出現在 `desired` 部分，但在 `reported` 部分卻沒有，則此屬性會包含在內。
+ 如果某個屬性在 `reported`部分被刪除，但仍存在於 `desired` 部分，則此屬性會包含在內。

### 範例 政策
<a name="update-delta-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingName/shadow/update/delta"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/update/delta"
            ]
        }
    ]
}
```

## /update/accepted
<a name="update-accepted-pub-sub-topic"></a>

AWS IoT 接受裝置影子的變更時， 會將回應狀態文件發佈至此主題：

```
ShadowTopicPrefix/update/accepted
```

訊息緩衝區包含 [/accepted response state document](device-shadow-document.md#device-shadow-example-response-json-accepted)。

### 範例 政策
<a name="update-accepted-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingName/shadow/update/accepted"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/update/accepted"
            ]
        }
    ]
}
```

## /update/documents
<a name="update-documents-pub-sub-topic"></a>

AWS IoT 每當成功執行影子更新時， 就會發佈狀態文件至此主題：

```
ShadowTopicPrefix/update/documents
```

訊息內文包含 [/documents response state document](device-shadow-document.md#device-shadow-example-response-json-documents)。

### 範例 政策
<a name="update-documents-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingName/shadow/update/documents"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/update/documents"
            ]
        }
    ]
}
```

## /update/rejected
<a name="update-rejected-pub-sub-topic"></a>

AWS IoT 當拒絕裝置影子的變更時， 會將錯誤回應文件發佈至此主題：

```
ShadowTopicPrefix/update/rejected
```

訊息內文包含 [錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)。

### 範例 政策
<a name="update-rejected-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingName/shadow/update/rejected"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/update/rejected"
            ]
        }
    ]
}
```

## /delete
<a name="delete-pub-sub-topic"></a>

若要刪除裝置的影子，發佈空白訊息即可刪除主題：

```
ShadowTopicPrefix/delete
```

訊息的內容會被忽略。

請注意，刪除影子並不會將其版本號碼重設為 0。

AWS IoT 透過發佈至 [/delete/accepted](#delete-accepted-pub-sub-topic)或 來回應 [/delete/rejected](#delete-rejected-pub-sub-topic)。

### 範例 政策
<a name="delete-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Publish"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/delete"
            ]
        }
    ]
}
```

## /delete/accepted
<a name="delete-accepted-pub-sub-topic"></a>

AWS IoT 刪除裝置的影子時， 會將訊息發佈至此主題：

```
ShadowTopicPrefix/delete/accepted
```

### 範例 政策
<a name="delete-accepted-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingName/shadow/delete/accepted"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/delete/accepted"
            ]
        }
    ]
}
```

## /delete/rejected
<a name="delete-rejected-pub-sub-topic"></a>

AWS IoT 當無法刪除裝置的影子時， 會將錯誤回應文件發佈至此主題：

```
ShadowTopicPrefix/delete/rejected
```

訊息內文包含 [錯誤回應文件](device-shadow-document.md#device-shadow-example-error-json)。

### 範例 政策
<a name="delete-rejected-policy"></a>

以下為所需政策的範例：

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/thingName/shadow/delete/rejected"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/thingName/shadow/delete/rejected"
            ]
        }
    ]
}
```

# 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" ]
    }
}
```

# Device Shadow 錯誤訊息
<a name="device-shadow-error-messages"></a>

當嘗試變更狀態文件失敗時，Device Shadow 服務會透過 MQTT 發佈訊息至錯誤主題。僅當對其中一個預留 `$aws` 主題的發佈請求回應時，此訊息才會進行發送。如果用戶端使用 REST API 更新文件，則會收到 HTTP 錯誤代碼作為部分回應，而 MQTT 錯誤訊息也不會發出。


****  

| HTTP 錯誤代碼 | 錯誤訊息 | 
| --- | --- | 
| 400 (錯誤的請求) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 401 (未經授權) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 403 (禁止) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 404 (未找到) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 409 (衝突) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 413 (承載過大) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 415 (不支援的媒體類型) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 429 (太多請求) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 500 (內部伺服器錯誤) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/iot/latest/developerguide/device-shadow-error-messages.html)  | 