@@ -66,6 +66,7 @@ def __init__(self):
66
66
self .lock = threading .Lock ()
67
67
self .shadow_value = None
68
68
self .disconnect_called = False
69
+ self .request_tokens = set ()
69
70
70
71
locked_data = LockedData ()
71
72
@@ -95,9 +96,15 @@ def on_disconnected(disconnect_future):
95
96
def on_get_shadow_accepted (response ):
96
97
# type: (iotshadow.GetShadowResponse) -> None
97
98
try :
98
- print ("Finished getting initial shadow state." )
99
-
100
99
with locked_data .lock :
100
+ # check that this is a response to a request from this session
101
+ try :
102
+ locked_data .request_tokens .remove (response .client_token )
103
+ except KeyError :
104
+ print ("Ignoring get_shadow_accepted message due to unexpected token." )
105
+ return
106
+
107
+ print ("Finished getting initial shadow state." )
101
108
if locked_data .shadow_value is not None :
102
109
print (" Ignoring initial query because a delta event has already been received." )
103
110
return
@@ -126,12 +133,24 @@ def on_get_shadow_accepted(response):
126
133
127
134
def on_get_shadow_rejected (error ):
128
135
# type: (iotshadow.ErrorResponse) -> None
129
- if error .code == 404 :
130
- print ("Thing has no shadow document. Creating with defaults..." )
131
- change_shadow_value (SHADOW_VALUE_DEFAULT )
132
- else :
133
- exit ("Get request was rejected. code:{} message:'{}'" .format (
134
- error .code , error .message ))
136
+ try :
137
+ # check that this is a response to a request from this session
138
+ with locked_data .lock :
139
+ try :
140
+ locked_data .request_tokens .remove (error .client_token )
141
+ except KeyError :
142
+ print ("Ignoring get_shadow_rejected message due to unexpected token." )
143
+ return
144
+
145
+ if error .code == 404 :
146
+ print ("Thing has no shadow document. Creating with defaults..." )
147
+ change_shadow_value (SHADOW_VALUE_DEFAULT )
148
+ else :
149
+ exit ("Get request was rejected. code:{} message:'{}'" .format (
150
+ error .code , error .message ))
151
+
152
+ except Exception as e :
153
+ exit (e )
135
154
136
155
def on_shadow_delta_updated (delta ):
137
156
# type: (iotshadow.ShadowDeltaUpdatedEvent) -> None
@@ -164,15 +183,39 @@ def on_publish_update_shadow(future):
164
183
def on_update_shadow_accepted (response ):
165
184
# type: (iotshadow.UpdateShadowResponse) -> None
166
185
try :
167
- print ("Finished updating reported shadow value to '{}'." .format (response .state .reported [shadow_property ])) # type: ignore
168
- print ("Enter desired value: " ) # remind user they can input new values
169
- except :
170
- exit ("Updated shadow is missing the target property." )
186
+ # check that this is a response to a request from this session
187
+ with locked_data .lock :
188
+ try :
189
+ locked_data .request_tokens .remove (response .client_token )
190
+ except KeyError :
191
+ print ("Ignoring update_shadow_accepted message due to unexpected token." )
192
+ return
193
+
194
+ try :
195
+ print ("Finished updating reported shadow value to '{}'." .format (response .state .reported [shadow_property ])) # type: ignore
196
+ print ("Enter desired value: " ) # remind user they can input new values
197
+ except :
198
+ exit ("Updated shadow is missing the target property." )
199
+
200
+ except Exception as e :
201
+ exit (e )
171
202
172
203
def on_update_shadow_rejected (error ):
173
204
# type: (iotshadow.ErrorResponse) -> None
174
- exit ("Update request was rejected. code:{} message:'{}'" .format (
175
- error .code , error .message ))
205
+ try :
206
+ # check that this is a response to a request from this session
207
+ with locked_data .lock :
208
+ try :
209
+ locked_data .request_tokens .remove (error .client_token )
210
+ except KeyError :
211
+ print ("Ignoring update_shadow_rejected message due to unexpected token." )
212
+ return
213
+
214
+ exit ("Update request was rejected. code:{} message:'{}'" .format (
215
+ error .code , error .message ))
216
+
217
+ except Exception as e :
218
+ exit (e )
176
219
177
220
def set_local_value_due_to_initial_query (reported_value ):
178
221
with locked_data .lock :
@@ -189,16 +232,25 @@ def change_shadow_value(value):
189
232
print ("Changed local shadow value to '{}'." .format (value ))
190
233
locked_data .shadow_value = value
191
234
192
- print ("Updating reported shadow value to '{}'..." .format (value ))
193
- request = iotshadow .UpdateShadowRequest (
194
- thing_name = thing_name ,
195
- state = iotshadow .ShadowState (
196
- reported = { shadow_property : value },
197
- desired = { shadow_property : value },
235
+ print ("Updating reported shadow value to '{}'..." .format (value ))
236
+
237
+ # use a unique token so we can correlate this "request" message to
238
+ # any "response" messages received on the /accepted and /rejected topics
239
+ token = str (uuid4 ())
240
+
241
+ request = iotshadow .UpdateShadowRequest (
242
+ thing_name = thing_name ,
243
+ state = iotshadow .ShadowState (
244
+ reported = { shadow_property : value },
245
+ desired = { shadow_property : value },
246
+ ),
247
+ client_token = token ,
198
248
)
199
- )
200
- future = shadow_client .publish_update_shadow (request , mqtt .QoS .AT_LEAST_ONCE )
201
- future .add_done_callback (on_publish_update_shadow )
249
+ future = shadow_client .publish_update_shadow (request , mqtt .QoS .AT_LEAST_ONCE )
250
+
251
+ locked_data .request_tokens .add (token )
252
+
253
+ future .add_done_callback (on_publish_update_shadow )
202
254
203
255
def user_input_thread_fn ():
204
256
while True :
@@ -318,14 +370,22 @@ def user_input_thread_fn():
318
370
# Wait for subscription to succeed
319
371
delta_subscribed_future .result ()
320
372
321
- # The rest of the sample runs asyncronously .
373
+ # The rest of the sample runs asynchronously .
322
374
323
375
# Issue request for shadow's current state.
324
376
# The response will be received by the on_get_accepted() callback
325
377
print ("Requesting current shadow state..." )
326
- publish_get_future = shadow_client .publish_get_shadow (
327
- request = iotshadow .GetShadowRequest (thing_name = args .thing_name ),
328
- qos = mqtt .QoS .AT_LEAST_ONCE )
378
+
379
+ with locked_data .lock :
380
+ # use a unique token so we can correlate this "request" message to
381
+ # any "response" messages received on the /accepted and /rejected topics
382
+ token = str (uuid4 ())
383
+
384
+ publish_get_future = shadow_client .publish_get_shadow (
385
+ request = iotshadow .GetShadowRequest (thing_name = args .thing_name , client_token = token ),
386
+ qos = mqtt .QoS .AT_LEAST_ONCE )
387
+
388
+ locked_data .request_tokens .add (token )
329
389
330
390
# Ensure that publish succeeds
331
391
publish_get_future .result ()
0 commit comments