

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

# Device Shadow 服务文档


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-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)

### 请求状态文档


请求状态文档具有以下格式：

```
{
    "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 服务拥有的最新版本相符时，该服务才会处理更新。

### 响应状态文档


响应状态文档具有以下格式，具体取决于响应类型。

#### /accepted 响应状态文档


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


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

#### /documents 响应状态文档


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

#### 响应状态文档属性

+ `previous` - 成功更新后，包含对象在更新之前的 `state`。
+ `current` - 成功更新后，包含对象在更新之后的 `state`。
+ `state`
  + `reported` - 只有在事物报告了 `reported` 部分中的任何数据时才存在，并且仅包含请求状态文档中的字段。
  + `desired` - 只有在设备报告了 `desired` 部分中的任何数据时才存在，并且仅包含请求状态文档中的字段。
+ `metadata` - 包含 `desired` 和 `reported` 部分中每个属性的时间戳，因此，您可以确定状态的更新时间。
+ `timestamp`— 生成响应的 Epoch 日期和时间。 AWS IoT
+ `clientToken` - 只有在向 `/update` 主题发布有效 JSON 时使用了客户端令牌时才存在。
+ `version` - AWS IoT中共享的设备影子文档的当前版本。它将在文档先前版本号的基础上增加一个数。

### 错误响应文档


错误响应文档具有以下格式：

```
{
    "code": error-code,
    "message": "error-message",
    "timestamp": timestamp,
    "clientToken": "token"
}
```
+ `code` - 用于表明错误类型的 HTTP 响应代码。
+ `message` - 用于提供额外信息的文本消息。
+ `timestamp`— 生成响应的日期和时间 AWS IoT。此属性并非在所有错误响应文档中都存在。
+ `clientToken` - 只有在发布的消息中使用了客户端令牌时才存在。

有关更多信息，请参阅 [Device Shadow 错误消息](device-shadow-error-messages.md)。

### 影子名称列表响应文档


影子名称列表响应文档具有以下格式：

```
{
    "results": [
        "shadowName-1",
        "shadowName-2",
        "shadowName-3",
        "shadowName-n"
    ],
    "nextToken": "nextToken",
    "timestamp": timestamp
}
```
+ `results` - 影子名称数组。
+ `nextToken` - 在获取序列中的下一页的分页请求中使用的令牌值。在没有其它要返回的影子名称时，该属性不存在。
+ `timestamp`— 生成响应的日期和时间 AWS IoT。

## 文档属性


设备的影子文档具有以下属性：

`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="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` 部分中的数组不匹配，则整个预期数组将被复制到增量中。

## 对影子文档进行版本控制


Device Shadow 服务支持在每个更新消息（请求和响应）中进行版本控制。这意味着，每次更新影子时，JSON 文档的版本将会增加。这可以确保两件事情：
+ 如果客户端尝试使用旧版本号覆盖影子，它将收到错误消息。客户端将被告知必须先进行重新同步，然后才能更新设备的影子。
+ 如果消息的版本比客户端存储的版本低，客户端则可决定不对该消息执行操作。

客户端可以在影子文档中不包含版本以绕过版本匹配。

## 影子文档中的客户端令牌


收发基于 MQTT 的消息时，您可以使用客户端令牌，以验证请求和请求响应是否包含相同的客户端令牌。这可以确保响应与请求相互关联。

**注意**  
客户端令牌不能超过 64 字节。超过 64 字节的客户端令牌将导致 400（错误请求）响应和 *Invalid clientToken*（无效的客户端令牌）错误消息。

## 空影子文档属性


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

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

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

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

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

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

影子文档也可能没有 `desired` 或 `reported` 属性，从而使影子文档为空。这是一个空影子（但有效）文档的示例。

```
{
}
```

## 影子文档中的数组值


影子支持数组，但将其视为正常值进行处理，因为对数组的更新将替换整个数组。无法更新数组的某个部分。

初始状态：

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

更新：

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

最终状态：

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

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

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