Skip to content

Commit 2b9ead0

Browse files
Rebuilt shadow bindings and updated shadow sample to show how to clear properties (#269)
* Rebuilt shadow bindings and updated shadow sample to show how to clear properties * Modified Shadow sample to fit model generator adjustments that allow passing None as valid input * Adjusted iotshadow so that the none_is_valid properties default to False * Minor adjustments to passing None in Shadow * Updated variable_is_null_valid to variable_is_nullable for better readability. Added setting _is_nullable to from_payload
1 parent 26563a4 commit 2b9ead0

File tree

2 files changed

+69
-9
lines changed

2 files changed

+69
-9
lines changed

awsiot/iotshadow.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -765,8 +765,10 @@ def __init__(self, *args, **kwargs):
765765
def to_payload(self):
766766
# type: () -> typing.Dict[str, typing.Any]
767767
payload = {} # type: typing.Dict[str, typing.Any]
768+
768769
if self.client_token is not None:
769770
payload['clientToken'] = self.client_token
771+
770772
return payload
771773

772774
class DeleteNamedShadowSubscriptionRequest(awsiot.ModeledClass):
@@ -824,8 +826,10 @@ def __init__(self, *args, **kwargs):
824826
def to_payload(self):
825827
# type: () -> typing.Dict[str, typing.Any]
826828
payload = {} # type: typing.Dict[str, typing.Any]
829+
827830
if self.client_token is not None:
828831
payload['clientToken'] = self.client_token
832+
829833
return payload
830834

831835
class DeleteShadowResponse(awsiot.ModeledClass):
@@ -977,8 +981,10 @@ def __init__(self, *args, **kwargs):
977981
def to_payload(self):
978982
# type: () -> typing.Dict[str, typing.Any]
979983
payload = {} # type: typing.Dict[str, typing.Any]
984+
980985
if self.client_token is not None:
981986
payload['clientToken'] = self.client_token
987+
982988
return payload
983989

984990
class GetNamedShadowSubscriptionRequest(awsiot.ModeledClass):
@@ -1036,8 +1042,10 @@ def __init__(self, *args, **kwargs):
10361042
def to_payload(self):
10371043
# type: () -> typing.Dict[str, typing.Any]
10381044
payload = {} # type: typing.Dict[str, typing.Any]
1045+
10391046
if self.client_token is not None:
10401047
payload['clientToken'] = self.client_token
1048+
10411049
return payload
10421050

10431051
class GetShadowResponse(awsiot.ModeledClass):
@@ -1295,15 +1303,22 @@ class ShadowState(awsiot.ModeledClass):
12951303
12961304
Attributes:
12971305
desired (typing.Dict[str, typing.Any]): The desired shadow state (from external services and devices).
1306+
desired_is_nullable (bool): Set to true to allow 'desired' to be None, clearing the data if sent.
1307+
12981308
reported (typing.Dict[str, typing.Any]): The (last) reported shadow state from the device.
1309+
reported_is_nullable (bool): Set to true to allow 'reported' to be None, clearing the data if sent.
1310+
12991311
"""
13001312

1301-
__slots__ = ['desired', 'reported']
1313+
__slots__ = ['desired', 'desired_is_nullable', 'reported', 'reported_is_nullable']
13021314

13031315
def __init__(self, *args, **kwargs):
13041316
self.desired = kwargs.get('desired')
13051317
self.reported = kwargs.get('reported')
13061318

1319+
self.desired_is_nullable = kwargs.get('desired_is_nullable', False)
1320+
self.reported_is_nullable = kwargs.get('reported_is_nullable', False)
1321+
13071322
# for backwards compatibility, read any arguments that used to be accepted by position
13081323
for key, val in zip(['desired', 'reported'], args):
13091324
setattr(self, key, val)
@@ -1318,15 +1333,28 @@ def from_payload(cls, payload):
13181333
val = payload.get('reported')
13191334
if val is not None:
13201335
new.reported = val
1336+
if new.desired == None:
1337+
new.desired_is_nullable = True
1338+
if new.reported == None:
1339+
new.reported_is_nullable = True
13211340
return new
13221341

13231342
def to_payload(self):
13241343
# type: () -> typing.Dict[str, typing.Any]
13251344
payload = {} # type: typing.Dict[str, typing.Any]
1326-
if self.desired is not None:
1345+
1346+
if self.desired_is_nullable is True:
13271347
payload['desired'] = self.desired
1328-
if self.reported is not None:
1348+
else:
1349+
if self.desired is not None:
1350+
payload['desired'] = self.desired
1351+
1352+
if self.reported_is_nullable is True:
13291353
payload['reported'] = self.reported
1354+
else:
1355+
if self.reported is not None:
1356+
payload['reported'] = self.reported
1357+
13301358
return payload
13311359

13321360
class ShadowStateWithDelta(awsiot.ModeledClass):
@@ -1522,12 +1550,16 @@ def __init__(self, *args, **kwargs):
15221550
def to_payload(self):
15231551
# type: () -> typing.Dict[str, typing.Any]
15241552
payload = {} # type: typing.Dict[str, typing.Any]
1553+
15251554
if self.client_token is not None:
15261555
payload['clientToken'] = self.client_token
1556+
15271557
if self.state is not None:
15281558
payload['state'] = self.state.to_payload()
1559+
15291560
if self.version is not None:
15301561
payload['version'] = self.version
1562+
15311563
return payload
15321564

15331565
class UpdateNamedShadowSubscriptionRequest(awsiot.ModeledClass):
@@ -1591,12 +1623,16 @@ def __init__(self, *args, **kwargs):
15911623
def to_payload(self):
15921624
# type: () -> typing.Dict[str, typing.Any]
15931625
payload = {} # type: typing.Dict[str, typing.Any]
1626+
15941627
if self.client_token is not None:
15951628
payload['clientToken'] = self.client_token
1629+
15961630
if self.state is not None:
15971631
payload['state'] = self.state.to_payload()
1632+
15981633
if self.version is not None:
15991634
payload['version'] = self.version
1635+
16001636
return payload
16011637

16021638
class UpdateShadowResponse(awsiot.ModeledClass):

samples/shadow.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,16 @@ def on_update_shadow_accepted(response):
192192
return
193193

194194
try:
195-
print("Finished updating reported shadow value to '{}'.".format(response.state.reported[shadow_property])) # type: ignore
195+
if response.state.reported != None:
196+
if shadow_property in response.state.reported:
197+
print("Finished updating reported shadow value to '{}'.".format(response.state.reported[shadow_property])) # type: ignore
198+
else:
199+
print ("Could not find shadow property with name: '{}'.".format(shadow_property)) # type: ignore
200+
else:
201+
print("Shadow states cleared.") # when the shadow states are cleared, reported and desired are set to None
196202
print("Enter desired value: ") # remind user they can input new values
197203
except:
198-
exit("Updated shadow is missing the target property.")
204+
exit("Updated shadow is missing the target property")
199205

200206
except Exception as e:
201207
exit(e)
@@ -238,14 +244,32 @@ def change_shadow_value(value):
238244
# any "response" messages received on the /accepted and /rejected topics
239245
token = str(uuid4())
240246

241-
request = iotshadow.UpdateShadowRequest(
247+
# if the value is "clear shadow" then send a UpdateShadowRequest with None
248+
# for both reported and desired to clear the shadow document completely
249+
# of both.
250+
if value == "clear_shadow":
251+
tmp_state = iotshadow.ShadowState(reported=None, desired=None, reported_is_nullable=True, desired_is_nullable=True)
252+
request = iotshadow.UpdateShadowRequest(
253+
thing_name=thing_name,
254+
state=tmp_state,
255+
client_token=token,
256+
)
257+
# Otherwise, send a normal update request
258+
else:
259+
# if the value is "none" then set it to a Python none object to
260+
# clear the individual shadow property
261+
if value == "none":
262+
value = None
263+
264+
request = iotshadow.UpdateShadowRequest(
242265
thing_name=thing_name,
243266
state=iotshadow.ShadowState(
244267
reported={ shadow_property: value },
245268
desired={ shadow_property: value },
246-
),
247-
client_token=token,
248-
)
269+
),
270+
client_token=token,
271+
)
272+
249273
future = shadow_client.publish_update_shadow(request, mqtt.QoS.AT_LEAST_ONCE)
250274

251275
locked_data.request_tokens.add(token)

0 commit comments

Comments
 (0)