

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

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

 AWS IoT Device Shadow 服务为 AWS IoT 事物对象添加阴影。无论设备是否已连接，Shadows 都可以将设备的状态提供给 AWS IoT 应用程序和其他服务。 AWS 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>

设备影子服务支持命名的影子和未命名或经典的影子。事物对象可以具有多个命名的影子，并且最多可以具有一个未命名的影子。事物对象也可以有一个预留命名影子，它的运行方式与命名影子类似，只是您无法更新其名称。有关更多信息，请参阅[预留命名影子](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 事物对象只能有一个未命名的阴影。如果预计您的物联网解决方案对影子数据的需求有限，您可能希望以这种方式开始使用影子。不过，如果您认为将来可能要添加其它影子，请考虑从一开始就使用命名的影子。

实例集索引支持不同的未命名影子和命名影子。有关更多信息，请参阅[管理实例集索引](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)，该 URL 支持对影子执行 `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 服务存储影子状态，在影子状态发生变化时发送消息，并对更改其状态的消息做出响应。您的物联网解决方案中的设备、应用程序和其它云服务必须管理其状态，并使其与设备影子的状态保持一致。

影子状态数据是动态的，有权访问影子的设备、应用程序和其它云服务可以对其进行更改。因此，请务必考虑每个设备、应用程序和其它云服务如何与影子进行交互。例如：
+ 在将状态数据传送到影子时，*设备* 应仅写入到影子状态的 `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 消息是设备与 Device Shadow 服务通信的 AWS IoT 首选方法。

影子通信使用 request/response MQTT 的发布/订阅通信模型来模拟模型。每个影子操作包含请求主题、成功的响应主题 (`accepted`) 和错误响应主题 (`rejected`)。

如果您希望应用程序和服务能够确定是否连接了设备，请参阅 [检测是否连接了设备](device-shadow-comms-app.md#thing-connection)。

**重要**  
由于 MQTT 使用 publish/subscribe 通信模型，因此您应该在发布请求主题*之前*订阅响应主题。否则，您不会收到您发布请求的响应。  
如果您使用调用 De [AWS IoT Device SDK](iot-sdks.md)vice Shadow 服务 APIs，则由您处理。

本节中的示例使用主题的缩写形式，其中*ShadowTopicPrefix*可以指已命名或未命名的影子，如下表所述。

影子可以是命名或未命名（经典）的。每个影子使用的主题仅在主题前缀上有所不同。下表显示每种影子类型使用的主题前缀。


| *ShadowTopicPrefix* 值 | 影子类型 | 
| --- | --- | 
| \$1aws/things/thingName/shadow | 未命名的（经典）影子 | 
| \$1aws/things/thingName/shadow/name/shadowName | 命名的影子 | 

**重要**  
确保应用程序或服务使用的影子与设备中的相应实施保持一致，并且该实施支持使用这些影子。例如，考虑如何创建、更新和删除影子。还要考虑如何在设备以及通过影子访问设备的应用程序或服务中处理更新。您的设计应明确指定如何更新和报告设备的状态，以及应用程序和服务如何与设备及其影子进行交互。

要创建完整的主题，请为要表示的影子类型选择 `ShadowTopicPrefix`，将 `thingName` 和 `shadowName`（如果适用）替换为相应的值，然后在其后面附加主题存根，如下表中所示。请记住，主题区分大小写。

有关影子的保留主题的更多信息，请参阅 [影子主题](reserved-topics.md#reserved-topics-shadow)。

## 在首次连接设备时初始化设备 AWS IoT
<a name="device-shadow-comms-device-first-connect"></a>

设备注册后 AWS IoT，应订阅这些 MQTT 消息以获取其支持的阴影。


| Topic | 含义 | 在收到该主题时设备应执行的操作 | 
| --- | --- | --- | 
|  `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` 主题。若要检索状态信息，请将 `/get` 请求用于命名影子 `$aws/things/My_IoT_Thing/shadow/name/namedShadow1/get`。

## 在设备连接时处理消息 AWS IoT
<a name="device-shadow-comms-device-while-connected"></a>

当设备连接时 AWS IoT，它可以接收 **/update/delta** 消息，并应通过以下方式使设备状态与其阴影中的变化保持一致：

1. 读取所有收到的 **/update/delta** 消息并同步设备状态以保持匹配。

1. 以 `reported` 消息正文发布 **/update** 消息，该消息正文包含设备的当前状态，无论设备状态何时发生变化。

在连接设备后，它应在出现指示时发布这些消息。


| 指示 | Topic | 有效载荷 | 
| --- | --- | --- | 
|  设备的状态已发生变化。  |  `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>

本节介绍应用程序或服务如何与 Dev AWS IoT ice Shadow 服务进行交互。该示例假定应用程序或服务仅与影子进行交互，并通过影子与设备进行交互。该示例不包括任何管理操作，例如创建或删除影子。

此示例使用 Dev AWS IoT ice Shadow 服务的 REST API 与阴影进行交互。与中使用的示例不同[在设备中使用影子](device-shadow-comms-device.md)，后者使用 REST API 的publish/subscribe communications model, this example uses the request/response通信模型。这意味着应用程序或服务必须先发出请求，然后才能收到来自的响应 AWS IoT。但该模型的一个缺点是，它不支持通知。如果您的应用程序或服务需要及时通知设备状态变化，请考虑支持 publish/subscribe 通信模型的 MQTT 或 MQTT over WSS 协议，如中所述。[在设备中使用影子](device-shadow-comms-device.md)

**重要**  
确保应用程序或服务使用的影子与设备中的相应实施保持一致，并且该实施支持使用这些影子。例如，考虑如何创建、更新和删除影子，以及如何在设备以及访问影子的应用程序或服务中处理更新。您的设计应明确指定如何更新和报告设备的状态，以及应用程序和服务如何与设备及其影子进行交互。

命名的影子的 REST API URL 为：

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

未命名的影子的 REST API URL 为：

```
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，它可以通过对其使用的阴影发送 HTTP GET 请求来定期查询当前状态。 URLs 

当最终用户与应用程序或服务交互以更改设备的状态时，该应用程序或服务可以向其用来更新影子`desired`状态 URLs 的影子发送 HTTP POST 请求。该请求返回已接受的更改，但您可能需要发出 HTTP GET 请求以轮询影子，直到设备使用新状态更新了影子为止。

## 检测是否连接了设备
<a name="thing-connection"></a>

要确定当前是否连接了设备，请在影子文档中包含一个 `connected` 属性；如果设备由于错误而断开连接，则使用 MQTT Last Will and Testament (LWT) 消息将 `connected` 属性设置为 `false`。

**注意**  
Dev AWS IoT ice Shadow 服务会忽略发送到 AWS IoT 保留主题（以 \$1 开头的主题）的 MQTT LWT 消息。但是，它们由订阅的客户端和 AWS IoT 规则引擎处理，因此您需要创建一条发送到非保留主题的 LWT 消息和一条规则，将 MQTT LWT 消息作为影子更新消息重新发布到影子的保留更新主题。`ShadowTopicPrefix/update`

**向 Device Shadow 服务发送 LWT 消息**

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`，它将 connected 属性设置为 `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 消息。重新发布规则检测该消息，并发布影子更新消息以更新设备影子的 `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 表示通过影子访问设备的应用程序或服务。该 AWS CLI 接口与应用可能用于通信的 API 非常相似 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)，然后在左侧菜单中选择**测试**以打开 **MQTT 客户端**。

1. 在另一个窗口中，在已安装 AWS CLI 的系统上打开一个终端窗口。

你应该打开两个窗口：一个在 “**测试**” 页面上显示 AWS IoT 控制台，另一个窗口带有命令行提示符。

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

在这个模拟中，我们将使用一个名为、的事物对象 *mySimulatedThing*，其阴影名为 *simShadow1*。

**创建事物对象及其物联网策略**  
要创建事物对象，请在 **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 控制台**中，在显示的事物列表中选择您的事物对象，然后选择 **Shadows**（影子）。

1. 选择 **Add a shadow**（添加影子），输入名称 `simShadow1`，然后选择 **Create**（创建）添加命名的影子。

**订阅并发布预留的 MQTT 主题**  
在控制台中，订阅预留的 MQTT 影子主题。这些主题是 `get`、`update` 和 `delete` 操作的响应，以便设备在发布操作后准备好接收响应。

**在 **MQTT 客户端**中订阅 MQTT 主题**

1. 在 **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 client**（MQTT 客户端）中发布 MQTT 主题**  
在设备初始化自身并订阅响应主题后，它应查询支持的影子。此模拟仅支持一个阴影，即支持名为 *simShadow1 的事物对象的阴影*。*mySimulatedThing*

**从 **MQTT 客户端**中获取当前影子状态**

1. 在 **MQTT 客户端**中，选择**发布到主题**。

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 client**（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 客户端**中，在 “**订阅**” 列aws/things/mySimulatedThing/shadow/name/simShadow1/update/delta中选择 **\$1**。如果主题名称被截断，您可以在其中暂停以查看完整主题。在本主题的主题日志中，您应该看到与此类似的 `/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 客户端**中，选择**发布到主题**。

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 客户端**的 **\$1 aws/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 （表示应用程序）和控制台（表示设备）之间的交互以模拟您的物联网解决方案。

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

本主题介绍了 AWS IoT 为处理影子而提供的三种方法的关联消息。这些方法包括：

`UPDATE`  <a name="update"></a>
创建影子（如果不存在），或使用消息正文中提供的状态信息更新现有影子的内容。 AWS IoT 记录每次更新的时间戳，以指示上次更新状态的时间。当影子的状态发生变化时， AWS IoT 会向所有 MQTT 订阅者发送带有`desired`和`reported`状态差异的`/delta`消息。收到 `/delta` 消息的设备或应用程序可以根据该差异执行操作。例如，设备可以将其状态更新为所需的状态，或者应用程序可以更新其 UI 以反映设备的状态变化。

`GET`  <a name="get"></a>
检索包含影子的完整状态的当前影子文档，包括元数据。

`DELETE`  <a name="delete"></a>
删除设备影子及其内容。  
您无法还原已删除的设备影子文档，但可以创建具有已删除设备影子文档的名称的新设备影子。如果您创建的设备影子文档与过去48小时内删除的文档同名，新设备影子文档的版本号和已删除文档的版本号相同。如果设备影子文档已删除超过 48 小时，具有相同名称的新设备影子文档的版本号为 0。

## 协议支持
<a name="protocol-support"></a>

AWS IoT 支持 [MQTT](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html) 和通过 HTTPS 协议的 REST API 来与阴影进行交互。 AWS IoT 为 MQTT 发布和订阅操作提供了一组保留的请求和响应主题。设备和应用程序应在发布请求主题之前订阅响应主题，以获取有关如何 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 and shadows 设计物联网解决方案时，应确定将请求更改的应用程序或设备以及将实施变更的应用程序或设备。通常，设备实施更改并向影子报告更改，而应用程序和服务响应并请求影子中的更改。您的解决方案可能有所不同，但本主题中的示例假定客户端应用程序或服务请求影子中的更改，而设备执行更改并向影子报告更改。

## 更新影子
<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 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted) 影子文档，而 `/update/delta` 消息包含一个 [/delta 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-delta) 影子文档。

1. 如果更新请求无效，则 AWS IoT 发布带有`$aws/things/thingName/shadow/name/shadowName/update/rejected`主题的消息以及描述错误的[错误响应文档](device-shadow-document.md#device-shadow-example-error-json)影子文档。

**在客户端使用 API 请求影子中的状态更改时**

1. 客户端调用 `UpdateThingShadow` API 并将 [请求状态文档](device-shadow-document.md#device-shadow-example-request-json) 状态文档作为其消息正文。

1. 如果请求有效，则 AWS IoT 返回一个 HTTP 成功响应代码和一个[/accepted 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文档作为其响应消息正文。

   AWS IoT 还将向该`$aws/things/thingName/shadow/name/shadowName/update/delta`主题发布 MQTT 消息，其中包含订阅该消息的任何设备或客户端的[/delta 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-delta)影子文档。

1. 如果请求无效，则 AWS IoT 返回 HTTP 错误响应代码 a [错误响应文档](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 接受更新，则会向`$aws/things/thingName/shadow/name/shadowName/update/accepted`主题发布带有[/accepted 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文档的消息。

1. 如果更新请求无效，则 AWS IoT 发布带有`$aws/things/thingName/shadow/name/shadowName/update/rejected`主题的消息以及描述错误的[错误响应文档](device-shadow-document.md#device-shadow-example-error-json)影子文档。

**在设备使用 API 向影子报告其当前状态时**

1. 设备调用 `UpdateThingShadow` API 并将 [请求状态文档](device-shadow-document.md#device-shadow-example-request-json) 状态文档作为其消息正文。

1. 如果请求有效，则 AWS IoT 更新影子并返回以影子文档作为其响应消息正文的 HTTP 成功响应代码。[/accepted 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)

   AWS IoT 还将向该`$aws/things/thingName/shadow/name/shadowName/update/delta`主题发布 MQTT 消息，其中包含订阅该消息的任何设备或客户端的[/delta 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-delta)影子文档。

1. 如果请求无效，则 AWS IoT 返回 HTTP 错误响应代码 a [错误响应文档](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. 如果请求成功，则向`$aws/things/thingName/shadow/name/shadowName/get/accepted`主题 AWS IoT 发布一条消息，消息正文[/accepted 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)中包含一个。

1. 如果请求无效，则向`$aws/things/thingName/shadow/name/shadowName/get/rejected`主题 AWS IoT 发布一条消息，并在消息正文[错误响应文档](device-shadow-document.md#device-shadow-example-error-json)中加上。

**使用 REST API 检索影子文档**

1. 设备或客户端使用空消息正文调用 `GetThingShadow` API。

1. 如果请求有效，则 AWS IoT 返回一个 HTTP 成功响应代码，其响应消息正文为[/accepted 响应状态文档](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) 主题。

**注意**  
删除影子不会立即将其版本号重置为 0。将在 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. 设备或客户端使用将 `null` 值分配给要删除的影子属性的状态文档以发布 `$aws/things/thingName/shadow/name/shadowName/update` 请求主题。只需在文档中包含要更改的属性。以下是删除 `engine` 属性的文档的示例。

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

1. 如果更新请求有效，则 AWS IoT 删除影子中的指定属性，并发布带有`$aws/things/thingName/shadow/name/shadowName/update/accepted`主题的消息，消息正文中包含[/accepted 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文档。

1. 如果更新请求无效，则 AWS IoT 发布带有`$aws/things/thingName/shadow/name/shadowName/update/rejected`主题的消息以及描述错误的[错误响应文档](device-shadow-document.md#device-shadow-example-error-json)影子文档。

**使用 REST API 从影子中删除属性**

1. 设备或客户端使用 [请求状态文档](device-shadow-document.md#device-shadow-example-request-json) 将 `null` 值分配给要删除的影子属性的以调用 `UpdateThingShadow` API。仅在文档中包含要删除的属性。以下是删除 `engine` 属性的文档的示例。

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

1. 如果请求有效，则 AWS IoT 返回一个 HTTP 成功响应代码和一个[/accepted 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文档作为其响应消息正文。

1. 如果请求无效，则 AWS IoT 返回 HTTP 错误响应代码 a [错误响应文档](device-shadow-document.md#device-shadow-example-error-json) 作为其响应消息正文。

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

以下是删除设备影子时的一些注意事项。
+ 将设备的影子状态设置为 `null` 并不会删除影子。在下次更新时，影子版本将会增加。
+ 删除设备的影子并不会删除事物对象。删除事物对象并不会删除相应设备的影子。
+ 删除影子不会立即将其版本号重置为 0。将在 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 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文档的消息。以下是接受的删除消息的示例：

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

1. 如果更新请求无效，则 AWS IoT 发布带有`$aws/things/thingName/shadow/name/shadowName/delete/rejected`主题的消息以及描述错误的[错误响应文档](device-shadow-document.md#device-shadow-example-error-json)影子文档。

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

1. 设备或客户端使用空消息缓冲区调用 `DeleteThingShadow` API。

1. 如果请求有效，则在消息正文中 AWS IoT 返回一个 HTTP 成功响应代码[/accepted 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)和一个缩写的[/accepted 响应状态文档](device-shadow-document.md#device-shadow-example-response-json-accepted)影子文档。以下是接受的删除消息的示例：

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

1. 如果请求无效，则 AWS IoT 返回 HTTP 错误响应代码 a [错误响应文档](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 details**（Device Shadow 详细信息）页面的 **Device Shadow URL** 部分查看端点 URI。

端点的格式如下：

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

影子 REST API 遵循与中所[设备通信协议](protocols.md)述相同的 HTTPS protocols/port 映射。

**注意**  
要使用 APIs，必须使用`iotdevicegateway`作为身份验证的服务名称。有关更多信息，请参阅 [Io TData 飞机](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 Version 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 Version 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 Version 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 Version 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`（如果适用）替换为相应的值，然后在其后面附加主题存根，如以下几部分中所示。

以下是用于与影子交互的 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)。

尝试更新设备状态的客户端将发送具有 `desired` 属性的 JSON 请求状态文档，例如：

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

更新其影子的设备将发送具有 `reported` 属性的 JSON 请求状态文档，例如：

```
{
  "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 响应状态文档](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 响应状态文档](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 响应状态文档](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>

Device Shadow 服务使用 R [EST API](device-shadow-rest-api.md) 或 [MQTT Pub/Sub 消息](device-shadow-mqtt.md)在更新、获取和删除操作中使用这些文档。

**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 响应状态文档
<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 响应状态文档
<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 响应状态文档
<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` - 只有在向 `/update` 主题发布有效 JSON 时使用了客户端令牌时才存在。
+ `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 Service Limits](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>

增量状态是一种虚拟类型的状态，包含 `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（错误请求）响应和 *Invalid clientToken*（无效的客户端令牌）错误消息。

## 空影子文档属性
<a name="device-shadow-empty-fields"></a>

如果影子文档中的 `reported` 和 `desired` 属性不适用于当前影子状态，它们可能为空或省略。例如，只有在影子文档具有所需状态时，它才包含 `desired` 属性。以下是没有 `desired` 属性的状态文档的有效示例：

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

`reported` 属性也可能为空，例如，设备尚未更新影子时：

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

如果更新导致 `desired` 或 `reported` 属性变为 null，则会将其从文档中删除。下面说明了如何将 `desired` 属性设置为 `null` 以删除该属性。例如，在设备更新其状态时，您可以执行该操作。

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

数组不能包含空值。例如，以下数组无效并将被拒绝。

```
{
    "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_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 401（未授权） |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 403（禁止访问） |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 404（未找到） |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 409（冲突） |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 413（有效载荷过大） |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 415（媒体类型不受支持） |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 429（请求过多） |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 
| 500（内部服务器错误） |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-error-messages.html)  | 