diff --git a/awsiot/iotshadow.py b/awsiot/iotshadow.py index eddc8bd0..60c6ff9b 100644 --- a/awsiot/iotshadow.py +++ b/awsiot/iotshadow.py @@ -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): @@ -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): @@ -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): @@ -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): @@ -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) @@ -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): @@ -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): @@ -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): diff --git a/samples/shadow.py b/samples/shadow.py index 61d06825..4df52bcb 100644 --- a/samples/shadow.py +++ b/samples/shadow.py @@ -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) @@ -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)