Skip to content

Commit

Permalink
Rebuilt shadow bindings and updated shadow sample to show how to clea…
Browse files Browse the repository at this point in the history
…r 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
  • Loading branch information
TwistedTwigleg authored Feb 17, 2022
1 parent 26563a4 commit 2b9ead0
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 9 deletions.
42 changes: 39 additions & 3 deletions awsiot/iotshadow.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,8 +765,10 @@ def __init__(self, *args, **kwargs):
def to_payload(self):
# type: () -> typing.Dict[str, typing.Any]
payload = {} # type: typing.Dict[str, typing.Any]

if self.client_token is not None:
payload['clientToken'] = self.client_token

return payload

class DeleteNamedShadowSubscriptionRequest(awsiot.ModeledClass):
Expand Down Expand Up @@ -824,8 +826,10 @@ def __init__(self, *args, **kwargs):
def to_payload(self):
# type: () -> typing.Dict[str, typing.Any]
payload = {} # type: typing.Dict[str, typing.Any]

if self.client_token is not None:
payload['clientToken'] = self.client_token

return payload

class DeleteShadowResponse(awsiot.ModeledClass):
Expand Down Expand Up @@ -977,8 +981,10 @@ def __init__(self, *args, **kwargs):
def to_payload(self):
# type: () -> typing.Dict[str, typing.Any]
payload = {} # type: typing.Dict[str, typing.Any]

if self.client_token is not None:
payload['clientToken'] = self.client_token

return payload

class GetNamedShadowSubscriptionRequest(awsiot.ModeledClass):
Expand Down Expand Up @@ -1036,8 +1042,10 @@ def __init__(self, *args, **kwargs):
def to_payload(self):
# type: () -> typing.Dict[str, typing.Any]
payload = {} # type: typing.Dict[str, typing.Any]

if self.client_token is not None:
payload['clientToken'] = self.client_token

return payload

class GetShadowResponse(awsiot.ModeledClass):
Expand Down Expand Up @@ -1295,15 +1303,22 @@ class ShadowState(awsiot.ModeledClass):
Attributes:
desired (typing.Dict[str, typing.Any]): The desired shadow state (from external services and devices).
desired_is_nullable (bool): Set to true to allow 'desired' to be None, clearing the data if sent.
reported (typing.Dict[str, typing.Any]): The (last) reported shadow state from the device.
reported_is_nullable (bool): Set to true to allow 'reported' to be None, clearing the data if sent.
"""

__slots__ = ['desired', 'reported']
__slots__ = ['desired', 'desired_is_nullable', 'reported', 'reported_is_nullable']

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

self.desired_is_nullable = kwargs.get('desired_is_nullable', False)
self.reported_is_nullable = kwargs.get('reported_is_nullable', False)

# for backwards compatibility, read any arguments that used to be accepted by position
for key, val in zip(['desired', 'reported'], args):
setattr(self, key, val)
Expand All @@ -1318,15 +1333,28 @@ def from_payload(cls, payload):
val = payload.get('reported')
if val is not None:
new.reported = val
if new.desired == None:
new.desired_is_nullable = True
if new.reported == None:
new.reported_is_nullable = True
return new

def to_payload(self):
# type: () -> typing.Dict[str, typing.Any]
payload = {} # type: typing.Dict[str, typing.Any]
if self.desired is not None:

if self.desired_is_nullable is True:
payload['desired'] = self.desired
if self.reported is not None:
else:
if self.desired is not None:
payload['desired'] = self.desired

if self.reported_is_nullable is True:
payload['reported'] = self.reported
else:
if self.reported is not None:
payload['reported'] = self.reported

return payload

class ShadowStateWithDelta(awsiot.ModeledClass):
Expand Down Expand Up @@ -1522,12 +1550,16 @@ def __init__(self, *args, **kwargs):
def to_payload(self):
# type: () -> typing.Dict[str, typing.Any]
payload = {} # type: typing.Dict[str, typing.Any]

if self.client_token is not None:
payload['clientToken'] = self.client_token

if self.state is not None:
payload['state'] = self.state.to_payload()

if self.version is not None:
payload['version'] = self.version

return payload

class UpdateNamedShadowSubscriptionRequest(awsiot.ModeledClass):
Expand Down Expand Up @@ -1591,12 +1623,16 @@ def __init__(self, *args, **kwargs):
def to_payload(self):
# type: () -> typing.Dict[str, typing.Any]
payload = {} # type: typing.Dict[str, typing.Any]

if self.client_token is not None:
payload['clientToken'] = self.client_token

if self.state is not None:
payload['state'] = self.state.to_payload()

if self.version is not None:
payload['version'] = self.version

return payload

class UpdateShadowResponse(awsiot.ModeledClass):
Expand Down
36 changes: 30 additions & 6 deletions samples/shadow.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,16 @@ def on_update_shadow_accepted(response):
return

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

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

request = iotshadow.UpdateShadowRequest(
# if the value is "clear shadow" then send a UpdateShadowRequest with None
# for both reported and desired to clear the shadow document completely
# of both.
if value == "clear_shadow":
tmp_state = iotshadow.ShadowState(reported=None, desired=None, reported_is_nullable=True, desired_is_nullable=True)
request = iotshadow.UpdateShadowRequest(
thing_name=thing_name,
state=tmp_state,
client_token=token,
)
# Otherwise, send a normal update request
else:
# if the value is "none" then set it to a Python none object to
# clear the individual shadow property
if value == "none":
value = None

request = iotshadow.UpdateShadowRequest(
thing_name=thing_name,
state=iotshadow.ShadowState(
reported={ shadow_property: value },
desired={ shadow_property: value },
),
client_token=token,
)
),
client_token=token,
)

future = shadow_client.publish_update_shadow(request, mqtt.QoS.AT_LEAST_ONCE)

locked_data.request_tokens.add(token)
Expand Down

0 comments on commit 2b9ead0

Please sign in to comment.