0
# Device Shadow Management
1
2
Device shadow operations for synchronizing device state between physical devices and the cloud. Provides shadow retrieval, updates, deletion, and delta notifications for state changes. Device shadows are JSON documents that store and retrieve current state information for a device.
3
4
## Capabilities
5
6
### Shadow Client Creation
7
8
Create a specialized MQTT client for device shadow operations with automatic subscription management and optimized for time-sensitive shadow messages.
9
10
```python { .api }
11
class AWSIoTMQTTShadowClient:
12
def __init__(self, clientID: str, protocolType: int = MQTTv3_1_1, useWebsocket: bool = False, cleanSession: bool = True, awsIoTMQTTClient = None):
13
"""
14
Create AWS IoT MQTT Shadow client.
15
16
Args:
17
clientID (str): Client identifier for MQTT connection
18
protocolType (int): MQTT version (MQTTv3_1=3, MQTTv3_1_1=4)
19
useWebsocket (bool): Enable MQTT over WebSocket SigV4
20
cleanSession (bool): Start with clean session state
21
awsIoTMQTTClient (AWSIoTMQTTClient): Existing MQTT client to reuse (optional)
22
"""
23
```
24
25
### Shadow Handler Creation
26
27
Create individual shadow handlers for specific devices with configurable subscription persistence.
28
29
```python { .api }
30
def createShadowHandlerWithName(self, shadowName: str, isPersistentSubscribe: bool) -> 'deviceShadow':
31
"""
32
Create device shadow handler for named shadow.
33
34
Args:
35
shadowName (str): Name of the device shadow
36
isPersistentSubscribe (bool): Whether to maintain persistent subscriptions to shadow topics
37
38
Returns:
39
deviceShadow: Shadow handler for shadow operations
40
"""
41
```
42
43
### Connection Management
44
45
Shadow clients inherit all connection management from the base MQTT client.
46
47
```python { .api }
48
def getMQTTConnection(self) -> 'AWSIoTMQTTClient':
49
"""
50
Get underlying MQTT client for direct MQTT operations.
51
52
Returns:
53
AWSIoTMQTTClient: The underlying MQTT client instance
54
"""
55
```
56
57
### Shadow Operations
58
59
Individual device shadow operations for state management and synchronization.
60
61
```python { .api }
62
class deviceShadow:
63
def shadowGet(self, callback: callable, timeout: int) -> str:
64
"""
65
Retrieve current shadow state from AWS IoT.
66
67
Args:
68
callback (callable): Response callback (topic, payload) -> None
69
timeout (int): Operation timeout in seconds
70
71
Returns:
72
str: Token for tracking this shadow operation
73
"""
74
75
def shadowUpdate(self, JSONPayload: str, callback: callable, timeout: int) -> str:
76
"""
77
Update shadow state in AWS IoT.
78
79
Args:
80
JSONPayload (str): JSON shadow update payload
81
callback (callable): Response callback (topic, payload) -> None
82
timeout (int): Operation timeout in seconds
83
84
Returns:
85
str: Token for tracking this shadow operation
86
"""
87
88
def shadowDelete(self, callback: callable, timeout: int) -> str:
89
"""
90
Delete shadow from AWS IoT.
91
92
Args:
93
callback (callable): Response callback (topic, payload) -> None
94
timeout (int): Operation timeout in seconds
95
96
Returns:
97
str: Token for tracking this shadow operation
98
"""
99
```
100
101
### Delta Notifications
102
103
Register for notifications when shadow desired state differs from reported state.
104
105
```python { .api }
106
def shadowRegisterDeltaCallback(self, callback: callable):
107
"""
108
Register callback for shadow delta events.
109
110
Args:
111
callback (callable): Delta callback (topic, payload) -> None
112
"""
113
114
def shadowUnregisterDeltaCallback(self):
115
"""Unregister delta callback for this shadow."""
116
```
117
118
## Usage Examples
119
120
### Basic Shadow Operations
121
122
```python
123
import AWSIoTPythonSDK.MQTTLib as AWSIoTPyMQTT
124
import json
125
import time
126
127
# Create shadow client
128
shadowClient = AWSIoTPyMQTT.AWSIoTMQTTShadowClient("myShadowClient")
129
shadowClient.configureEndpoint("endpoint.iot.region.amazonaws.com", 8883)
130
shadowClient.configureCredentials("rootCA.crt", "private.key", "certificate.crt")
131
132
# Configure connection
133
shadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
134
shadowClient.configureConnectDisconnectTimeout(10)
135
shadowClient.configureMQTTOperationTimeout(5)
136
137
# Connect to AWS IoT
138
shadowClient.connect()
139
140
# Create device shadow handler
141
deviceShadowHandler = shadowClient.createShadowHandlerWithName("myDevice", True)
142
143
# Shadow response callbacks
144
def shadowUpdateCallback(payload, responseStatus, token):
145
if responseStatus == "timeout":
146
print("Shadow update request timed out")
147
elif responseStatus == "accepted":
148
print("Shadow update accepted")
149
elif responseStatus == "rejected":
150
print("Shadow update rejected")
151
152
def shadowGetCallback(payload, responseStatus, token):
153
if responseStatus == "timeout":
154
print("Shadow get request timed out")
155
elif responseStatus == "accepted":
156
payloadDict = json.loads(payload)
157
print(f"Shadow state: {payloadDict}")
158
elif responseStatus == "rejected":
159
print("Shadow get rejected")
160
161
# Get current shadow state
162
deviceShadowHandler.shadowGet(shadowGetCallback, 5)
163
164
# Update shadow state
165
shadowPayload = {
166
"state": {
167
"desired": {
168
"temperature": 25.0,
169
"humidity": 60.0
170
},
171
"reported": {
172
"temperature": 23.5,
173
"humidity": 58.0
174
}
175
}
176
}
177
178
deviceShadowHandler.shadowUpdate(json.dumps(shadowPayload), shadowUpdateCallback, 5)
179
```
180
181
### Delta Handling
182
183
```python
184
import AWSIoTPythonSDK.MQTTLib as AWSIoTPyMQTT
185
import json
186
187
# Create and configure shadow client
188
shadowClient = AWSIoTPyMQTT.AWSIoTMQTTShadowClient("deltaClient")
189
shadowClient.configureEndpoint("endpoint.iot.region.amazonaws.com", 8883)
190
shadowClient.configureCredentials("rootCA.crt", "private.key", "certificate.crt")
191
shadowClient.connect()
192
193
# Create shadow handler
194
deviceShadowHandler = shadowClient.createShadowHandlerWithName("myDevice", True)
195
196
# Delta callback - called when desired state differs from reported state
197
def shadowDeltaCallback(payload, responseStatus, token):
198
print(f"Received shadow delta: {payload}")
199
deltaPayload = json.loads(payload)
200
201
# Extract delta state changes
202
if "state" in deltaPayload:
203
deltaState = deltaPayload["state"]
204
print(f"Delta state changes: {deltaState}")
205
206
# Apply delta changes to device
207
for key, value in deltaState.items():
208
print(f"Updating device {key} to {value}")
209
# Implement device-specific logic here
210
211
# Report updated state back to shadow
212
reportedPayload = {
213
"state": {
214
"reported": deltaState
215
}
216
}
217
deviceShadowHandler.shadowUpdate(json.dumps(reportedPayload), shadowUpdateCallback, 5)
218
219
def shadowUpdateCallback(payload, responseStatus, token):
220
if responseStatus == "accepted":
221
print("Device state updated successfully")
222
223
# Register for delta notifications
224
deviceShadowHandler.shadowRegisterDeltaCallback(shadowDeltaCallback)
225
226
# Keep client running to receive delta notifications
227
try:
228
while True:
229
time.sleep(1)
230
except KeyboardInterrupt:
231
deviceShadowHandler.shadowUnregisterDeltaCallback()
232
shadowClient.disconnect()
233
```
234
235
### Multiple Shadow Management
236
237
```python
238
import AWSIoTPythonSDK.MQTTLib as AWSIoTPyMQTT
239
import json
240
241
# Create shadow client
242
shadowClient = AWSIoTPyMQTT.AWSIoTMQTTShadowClient("multiShadowClient")
243
shadowClient.configureEndpoint("endpoint.iot.region.amazonaws.com", 8883)
244
shadowClient.configureCredentials("rootCA.crt", "private.key", "certificate.crt")
245
shadowClient.connect()
246
247
# Create multiple shadow handlers
248
thermostatShadow = shadowClient.createShadowHandlerWithName("thermostat", True)
249
lightingShadow = shadowClient.createShadowHandlerWithName("lighting", True)
250
securityShadow = shadowClient.createShadowHandlerWithName("security", True)
251
252
# Shadow-specific callbacks
253
def thermostatCallback(payload, responseStatus, token):
254
if responseStatus == "accepted":
255
print(f"Thermostat shadow updated: {payload}")
256
257
def lightingCallback(payload, responseStatus, token):
258
if responseStatus == "accepted":
259
print(f"Lighting shadow updated: {payload}")
260
261
def securityCallback(payload, responseStatus, token):
262
if responseStatus == "accepted":
263
print(f"Security shadow updated: {payload}")
264
265
# Update each shadow independently
266
thermostat_state = {
267
"state": {
268
"reported": {
269
"temperature": 22.0,
270
"target_temperature": 24.0
271
}
272
}
273
}
274
275
lighting_state = {
276
"state": {
277
"reported": {
278
"brightness": 80,
279
"color": {"r": 255, "g": 255, "b": 255}
280
}
281
}
282
}
283
284
security_state = {
285
"state": {
286
"reported": {
287
"armed": True,
288
"sensors": {"door": "closed", "motion": "inactive"}
289
}
290
}
291
}
292
293
# Perform shadow updates
294
thermostatShadow.shadowUpdate(json.dumps(thermostat_state), thermostatCallback, 5)
295
lightingShadow.shadowUpdate(json.dumps(lighting_state), lightingCallback, 5)
296
securityShadow.shadowUpdate(json.dumps(security_state), securityCallback, 5)
297
```
298
299
### Shadow Document Structure
300
301
```python
302
# Example shadow document structure
303
shadow_document = {
304
"state": {
305
"desired": {
306
# Desired device state set by applications
307
"temperature": 25.0,
308
"mode": "heat"
309
},
310
"reported": {
311
# Current device state reported by device
312
"temperature": 23.5,
313
"mode": "heat",
314
"connected": True
315
},
316
"delta": {
317
# Difference between desired and reported (read-only)
318
"temperature": 1.5
319
}
320
},
321
"metadata": {
322
# Timestamps and version information
323
"desired": {
324
"temperature": {"timestamp": 1609459200},
325
"mode": {"timestamp": 1609459200}
326
},
327
"reported": {
328
"temperature": {"timestamp": 1609459180},
329
"mode": {"timestamp": 1609459180},
330
"connected": {"timestamp": 1609459180}
331
}
332
},
333
"version": 123,
334
"timestamp": 1609459200
335
}
336
```
337
338
## Types
339
340
```python { .api }
341
# Shadow response callback signature
342
def shadowCallback(payload: str, responseStatus: str, token: str) -> None:
343
"""
344
Shadow operation response callback.
345
346
Args:
347
payload (str): JSON response payload
348
responseStatus (str): "accepted", "rejected", or "timeout"
349
token (str): Token from the shadow operation
350
"""
351
352
# Shadow delta callback signature
353
def shadowDeltaCallback(payload: str, responseStatus: str, token: str) -> None:
354
"""
355
Shadow delta notification callback.
356
357
Args:
358
payload (str): JSON delta payload with state differences
359
responseStatus (str): Always "delta" for delta callbacks
360
token (str): Token from delta notification
361
"""
362
363
# Shadow operation response statuses
364
SHADOW_ACCEPTED = "accepted"
365
SHADOW_REJECTED = "rejected"
366
SHADOW_TIMEOUT = "timeout"
367
SHADOW_DELTA = "delta"
368
```