diff --git a/AWSIoTPythonSDK/MQTTLib.py b/AWSIoTPythonSDK/MQTTLib.py index 0b2a734..083ad54 100755 --- a/AWSIoTPythonSDK/MQTTLib.py +++ b/AWSIoTPythonSDK/MQTTLib.py @@ -94,6 +94,7 @@ def configureLastWill(self, topic, payload, QoS): **Syntax** .. code:: python + myAWSIoTMQTTClient.configureLastWill("last/Will/Topic", "lastWillPayload", 0) **Parameters** @@ -120,7 +121,8 @@ def clearLastWill(self): **Syntax** - ..code:: python + .. code:: python + myAWSIoTMQTTClient.clearLastWill() **Parameter** @@ -438,8 +440,8 @@ def publish(self, topic, payload, QoS): # Publish a QoS0 message "myPayload" to topic "myToppic" myAWSIoTMQTTClient.publish("myTopic", "myPayload", 0) - # Publish a QoS1 message "myPayload2" to topic "myTopic/sub" - myAWSIoTMQTTClient.publish("myTopic/sub", "myPayload", 1) + # Publish a QoS1 message "myPayloadWithQos1" to topic "myTopic/sub" + myAWSIoTMQTTClient.publish("myTopic/sub", "myPayloadWithQos1", 1) **Parameters** @@ -582,6 +584,7 @@ def configureLastWill(self, topic, payload, QoS): **Syntax** .. code:: python + myAWSIoTMQTTClient.configureLastWill("last/Will/Topic", "lastWillPayload", 0) **Parameters** @@ -608,7 +611,8 @@ def clearLastWill(self): **Syntax** - ..code:: python + .. code:: python + myAWSIoTShadowMQTTClient.clearLastWill() **Parameter** @@ -784,7 +788,7 @@ def connect(self, keepAliveIntervalSecond=30): """ **Description** - Connect to AWS IoT, with user-specific keeoalive interval configuration. + Connect to AWS IoT, with user-specific keepalive interval configuration. **Syntax** diff --git a/AWSIoTPythonSDK/__init__.py b/AWSIoTPythonSDK/__init__.py index 67927ad..3925732 100755 --- a/AWSIoTPythonSDK/__init__.py +++ b/AWSIoTPythonSDK/__init__.py @@ -1,6 +1,6 @@ import os import sys -__version__ = "1.1.1" +__version__ = "1.1.2" diff --git a/AWSIoTPythonSDK/core/protocol/paho/client.py b/AWSIoTPythonSDK/core/protocol/paho/client.py index 53852db..6096aa4 100755 --- a/AWSIoTPythonSDK/core/protocol/paho/client.py +++ b/AWSIoTPythonSDK/core/protocol/paho/client.py @@ -804,7 +804,13 @@ def reconnect(self): ssl.match_hostname(self._ssl.getpeercert(), self._host) self._sock = sock - self._sock.setblocking(0) + + if self._ssl and not self._useSecuredWebsocket: + self._ssl.setblocking(0) # For X.509 cert mutual auth. + elif not self._ssl: + self._sock.setblocking(0) # For plain socket + else: + pass # For MQTT over WebSocket return self._send_connect(self._keepalive, self._clean_session) diff --git a/AWSIoTPythonSDK/core/shadow/deviceShadow.py b/AWSIoTPythonSDK/core/shadow/deviceShadow.py index e7997f6..4404aa8 100755 --- a/AWSIoTPythonSDK/core/shadow/deviceShadow.py +++ b/AWSIoTPythonSDK/core/shadow/deviceShadow.py @@ -13,29 +13,22 @@ # * permissions and limitations under the License. # */ -import sys import json -import string -import random import logging +import uuid from threading import Timer, Lock, Thread class _shadowRequestToken: + URN_PREFIX_LENGTH = 9 + def __init__(self, srcShadowName, srcClientID): self._shadowName = srcShadowName self._clientID = srcClientID - self._sequenceNumber = 0 - self._lowercase = string.ascii_lowercase def getNextToken(self): - ret = self._clientID + "_" + self._shadowName + "_" + str(self._sequenceNumber) + "_" + self._randomString(5) - self._sequenceNumber += 1 - return ret - - def _randomString(self, lengthOfString): - return "".join(random.choice(self._lowercase) for i in range(lengthOfString)) + return uuid.uuid4().urn[self.URN_PREFIX_LENGTH:] # We only need the uuid digits, not the urn prefix class _basicJSONParser: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a128c27..9c48d50 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,15 @@ CHANGELOG ========= +1.1.2 +===== +* bugfix:Issue:`#28 `__ +* bugfix:Issue:`#29 `__ +* bugfix:Pull request:`#32 `__ +* improvement:Pull request:`#38 `__ +* bugfix:Pull request:`#45 `__ +* improvement:Pull request:`#46 `__ + 1.1.1 ===== * bugfix:Issue:`#23 `__ diff --git a/NOTICE.txt b/NOTICE.txt index ee3d6e1..0bf7141 100755 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ AWS IoT Python SDK for Internet of Things Service -Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. This product includes software developed by Amazon Inc (http://www.amazon.com/). \ No newline at end of file diff --git a/README.rst b/README.rst index 1bc992e..6a7eaf1 100755 --- a/README.rst +++ b/README.rst @@ -510,6 +510,8 @@ Run the example like this: python basicPubSub.py -e -r -c -k # MQTT over WebSocket python basicPubSub.py -e -r -w + # Customize client id and topic + python basicPubSub.py -e -r -c -k -id -t Source ****** @@ -541,6 +543,8 @@ Run the example like this: .. code-block:: python python basicPubSub_CognitoSTS.py -e -r -C + # Customize client id and topic + python basicPubsub_CognitoSTS.py -e -r -C -id -t Source ****** @@ -590,6 +594,7 @@ Then, start the basicShadowUpdater: # MQTT over WebSocket python basicShadowUpdater.py -e -r -w + After the basicShadowUpdater starts sending shadow update requests, you should be able to see corresponding delta messages in the basicShadowDeltaListener output. @@ -621,6 +626,8 @@ Run the example like this: python ThingShadowEcho.py -e -r -c -k # MQTT over WebSocket python ThingShadowEcho.py -e -r -w + # Customize client Id and thing name + python ThingShadowEcho.py -e -r -c -k -id -n Now use the `AWS IoT console `__ or other MQTT client to update the shadow desired state only. You should be able to see the reported state is updated to match diff --git a/samples/ThingShadowEcho/ThingShadowEcho.py b/samples/ThingShadowEcho/ThingShadowEcho.py index a0b76bc..a026ca9 100755 --- a/samples/ThingShadowEcho/ThingShadowEcho.py +++ b/samples/ThingShadowEcho/ThingShadowEcho.py @@ -20,7 +20,7 @@ import logging import time import json -import getopt +import argparse class shadowCallbackContainer: def __init__(self, deviceShadowInstance): @@ -39,79 +39,32 @@ def customShadowCallback_Delta(self, payload, responseStatus, token): self.deviceShadowInstance.shadowUpdate(newPayload, None, 5) print("Sent.") -# Usage -usageInfo = """Usage: - -Use certificate based mutual authentication: -python ThingShadowEcho.py -e -r -c -k - -Use MQTT over WebSocket: -python ThingShadowEcho.py -e -r -w -Type "python ThingShadowEcho.py -h" for available options. - - -""" -# Help info -helpInfo = """-e, --endpoint - Your AWS IoT custom endpoint --r, --rootCA - Root CA file path --c, --cert - Certificate file path --k, --key - Private key file path --w, --websocket - Use MQTT over WebSocket --h, --help - Help information - - -""" - # Read in command-line parameters -useWebsocket = False -host = "" -rootCAPath = "" -certificatePath = "" -privateKeyPath = "" -try: - opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"]) - if len(opts) == 0: - raise getopt.GetoptError("No input parameters!") - for opt, arg in opts: - if opt in ("-h", "--help"): - print(helpInfo) - exit(0) - if opt in ("-e", "--endpoint"): - host = arg - if opt in ("-r", "--rootCA"): - rootCAPath = arg - if opt in ("-c", "--cert"): - certificatePath = arg - if opt in ("-k", "--key"): - privateKeyPath = arg - if opt in ("-w", "--websocket"): - useWebsocket = True -except getopt.GetoptError: - print(usageInfo) - exit(1) +parser = argparse.ArgumentParser() +parser.add_argument("-e", "--endpoint", action="store", required=True, dest="host", help="Your AWS IoT custom endpoint") +parser.add_argument("-r", "--rootCA", action="store", required=True, dest="rootCAPath", help="Root CA file path") +parser.add_argument("-c", "--cert", action="store", dest="certificatePath", help="Certificate file path") +parser.add_argument("-k", "--key", action="store", dest="privateKeyPath", help="Private key file path") +parser.add_argument("-w", "--websocket", action="store_true", dest="useWebsocket", default=False, + help="Use MQTT over WebSocket") +parser.add_argument("-n", "--thingName", action="store", dest="thingName", default="Bot", help="Targeted thing name") +parser.add_argument("-id", "--clientId", action="store", dest="clientId", default="ThingShadowEcho", help="Targeted client id") + +args = parser.parse_args() +host = args.host +rootCAPath = args.rootCAPath +certificatePath = args.certificatePath +privateKeyPath = args.privateKeyPath +useWebsocket = args.useWebsocket +thingName = args.thingName +clientId = args.clientId + +if args.useWebsocket and args.certificatePath and args.privateKeyPath: + parser.error("X.509 cert authentication and WebSocket are mutual exclusive. Please pick one.") + exit(2) -# Missing configuration notification -missingConfiguration = False -if not host: - print("Missing '-e' or '--endpoint'") - missingConfiguration = True -if not rootCAPath: - print("Missing '-r' or '--rootCA'") - missingConfiguration = True -if not useWebsocket: - if not certificatePath: - print("Missing '-c' or '--cert'") - missingConfiguration = True - if not privateKeyPath: - print("Missing '-k' or '--key'") - missingConfiguration = True -if missingConfiguration: +if not args.useWebsocket and (not args.certificatePath or not args.privateKeyPath): + parser.error("Missing credentials for authentication.") exit(2) # Configure logging @@ -125,11 +78,11 @@ def customShadowCallback_Delta(self, payload, responseStatus, token): # Init AWSIoTMQTTShadowClient myAWSIoTMQTTShadowClient = None if useWebsocket: - myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("ThingShadowEcho", useWebsocket=True) + myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId, useWebsocket=True) myAWSIoTMQTTShadowClient.configureEndpoint(host, 443) myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath) else: - myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("ThingShadowEcho") + myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId) myAWSIoTMQTTShadowClient.configureEndpoint(host, 8883) myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath) @@ -142,12 +95,12 @@ def customShadowCallback_Delta(self, payload, responseStatus, token): myAWSIoTMQTTShadowClient.connect() # Create a deviceShadow with persistent subscription -Bot = myAWSIoTMQTTShadowClient.createShadowHandlerWithName("Bot", True) -shadowCallbackContainer_Bot = shadowCallbackContainer(Bot) +deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True) +shadowCallbackContainer_Bot = shadowCallbackContainer(deviceShadowHandler) # Listen on deltas -Bot.shadowRegisterDeltaCallback(shadowCallbackContainer_Bot.customShadowCallback_Delta) +deviceShadowHandler.shadowRegisterDeltaCallback(shadowCallbackContainer_Bot.customShadowCallback_Delta) # Loop forever while True: - pass + time.sleep(1) diff --git a/samples/basicPubSub/basicPubSub.py b/samples/basicPubSub/basicPubSub.py index 1e044b8..1ef4e84 100755 --- a/samples/basicPubSub/basicPubSub.py +++ b/samples/basicPubSub/basicPubSub.py @@ -19,7 +19,7 @@ import sys import logging import time -import getopt +import argparse # Custom MQTT message callback def customCallback(client, userdata, message): @@ -29,78 +29,32 @@ def customCallback(client, userdata, message): print(message.topic) print("--------------\n\n") -# Usage -usageInfo = """Usage: - -Use certificate based mutual authentication: -python basicPubSub.py -e -r -c -k - -Use MQTT over WebSocket: -python basicPubSub.py -e -r -w - -Type "python basicPubSub.py -h" for available options. -""" -# Help info -helpInfo = """-e, --endpoint - Your AWS IoT custom endpoint --r, --rootCA - Root CA file path --c, --cert - Certificate file path --k, --key - Private key file path --w, --websocket - Use MQTT over WebSocket --h, --help - Help information - +# Read in command-line parameters +parser = argparse.ArgumentParser() +parser.add_argument("-e", "--endpoint", action="store", required=True, dest="host", help="Your AWS IoT custom endpoint") +parser.add_argument("-r", "--rootCA", action="store", required=True, dest="rootCAPath", help="Root CA file path") +parser.add_argument("-c", "--cert", action="store", dest="certificatePath", help="Certificate file path") +parser.add_argument("-k", "--key", action="store", dest="privateKeyPath", help="Private key file path") +parser.add_argument("-w", "--websocket", action="store_true", dest="useWebsocket", default=False, + help="Use MQTT over WebSocket") +parser.add_argument("-id", "--clientId", action="store", dest="clientId", default="basicPubSub", help="Targeted client id") +parser.add_argument("-t", "--topic", action="store", dest="topic", default="sdk/test/Python", help="Targeted topic") -""" +args = parser.parse_args() +host = args.host +rootCAPath = args.rootCAPath +certificatePath = args.certificatePath +privateKeyPath = args.privateKeyPath +useWebsocket = args.useWebsocket +clientId = args.clientId +topic = args.topic -# Read in command-line parameters -useWebsocket = False -host = "" -rootCAPath = "" -certificatePath = "" -privateKeyPath = "" -try: - opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"]) - if len(opts) == 0: - raise getopt.GetoptError("No input parameters!") - for opt, arg in opts: - if opt in ("-h", "--help"): - print(helpInfo) - exit(0) - if opt in ("-e", "--endpoint"): - host = arg - if opt in ("-r", "--rootCA"): - rootCAPath = arg - if opt in ("-c", "--cert"): - certificatePath = arg - if opt in ("-k", "--key"): - privateKeyPath = arg - if opt in ("-w", "--websocket"): - useWebsocket = True -except getopt.GetoptError: - print(usageInfo) - exit(1) +if args.useWebsocket and args.certificatePath and args.privateKeyPath: + parser.error("X.509 cert authentication and WebSocket are mutual exclusive. Please pick one.") + exit(2) -# Missing configuration notification -missingConfiguration = False -if not host: - print("Missing '-e' or '--endpoint'") - missingConfiguration = True -if not rootCAPath: - print("Missing '-r' or '--rootCA'") - missingConfiguration = True -if not useWebsocket: - if not certificatePath: - print("Missing '-c' or '--cert'") - missingConfiguration = True - if not privateKeyPath: - print("Missing '-k' or '--key'") - missingConfiguration = True -if missingConfiguration: +if not args.useWebsocket and (not args.certificatePath or not args.privateKeyPath): + parser.error("Missing credentials for authentication.") exit(2) # Configure logging @@ -114,11 +68,11 @@ def customCallback(client, userdata, message): # Init AWSIoTMQTTClient myAWSIoTMQTTClient = None if useWebsocket: - myAWSIoTMQTTClient = AWSIoTMQTTClient("basicPubSub", useWebsocket=True) + myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId, useWebsocket=True) myAWSIoTMQTTClient.configureEndpoint(host, 443) myAWSIoTMQTTClient.configureCredentials(rootCAPath) else: - myAWSIoTMQTTClient = AWSIoTMQTTClient("basicPubSub") + myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId) myAWSIoTMQTTClient.configureEndpoint(host, 8883) myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath) @@ -131,12 +85,12 @@ def customCallback(client, userdata, message): # Connect and subscribe to AWS IoT myAWSIoTMQTTClient.connect() -myAWSIoTMQTTClient.subscribe("sdk/test/Python", 1, customCallback) +myAWSIoTMQTTClient.subscribe(topic, 1, customCallback) time.sleep(2) # Publish to the same topic in a loop forever loopCount = 0 while True: - myAWSIoTMQTTClient.publish("sdk/test/Python", "New Message " + str(loopCount), 1) + myAWSIoTMQTTClient.publish(topic, "New Message " + str(loopCount), 1) loopCount += 1 time.sleep(1) diff --git a/samples/basicPubSub/basicPubSub_CognitoSTS.py b/samples/basicPubSub/basicPubSub_CognitoSTS.py index 988bb36..f6d20e7 100755 --- a/samples/basicPubSub/basicPubSub_CognitoSTS.py +++ b/samples/basicPubSub/basicPubSub_CognitoSTS.py @@ -20,7 +20,7 @@ import sys import logging import time -import getopt +import argparse # Custom MQTT message callback def customCallback(client, userdata, message): @@ -30,62 +30,20 @@ def customCallback(client, userdata, message): print(message.topic) print("--------------\n\n") -# Usage -usageInfo = """Usage: - -python basicPubSub_CognitoSTS.py -e -r -C - - -Type "python basicPubSub_CognitoSTS.py -h" for available options. -""" -# Help info -helpInfo = """-e, --endpoint - Your AWS IoT custom endpoint --r, --rootCA - Root CA file path --C, --CognitoIdentityPoolID - Your AWS Cognito Identity Pool ID --h, --help - Help information - - -""" - # Read in command-line parameters -host = "" -rootCAPath = "" -cognitoIdentityPoolID = "" -try: - opts, args = getopt.getopt(sys.argv[1:], "he:r:C:", ["help", "endpoint=", "rootCA=", "CognitoIdentityPoolID="]) - if len(opts) == 0: - raise getopt.GetoptError("No input parameters!") - for opt, arg in opts: - if opt in ("-h", "--help"): - print(helpInfo) - exit(0) - if opt in ("-e", "--endpoint"): - host = arg - if opt in ("-r", "--rootCA"): - rootCAPath = arg - if opt in ("-C", "--CognitoIdentityPoolID"): - cognitoIdentityPoolID = arg -except getopt.GetoptError: - print(usageInfo) - exit(1) - -# Missing configuration notification -missingConfiguration = False -if not host: - print("Missing '-e' or '--endpoint'") - missingConfiguration = True -if not rootCAPath: - print("Missing '-r' or '--rootCA'") - missingConfiguration = True -if not cognitoIdentityPoolID: - print("Missing '-C' or '--CognitoIdentityPoolID'") - missingConfiguration = True -if missingConfiguration: - exit(2) +parser = argparse.ArgumentParser() +parser.add_argument("-e", "--endpoint", action="store", required=True, dest="host", help="Your AWS IoT custom endpoint") +parser.add_argument("-r", "--rootCA", action="store", required=True, dest="rootCAPath", help="Root CA file path") +parser.add_argument("-C", "--CognitoIdentityPoolID", action="store", required=True, dest="cognitoIdentityPoolID", help="Your AWS Cognito Identity Pool ID") +parser.add_argument("-id", "--clientId", action="store", dest="clientId", default="basicPubSub_CognitoSTS", help="Targeted client id") +parser.add_argument("-t", "--topic", action="store", dest="topic", default="sdk/test/Python", help="Targeted topic") + +args = parser.parse_args() +host = args.host +rootCAPath = args.rootCAPath +clientId = args.clientId +cognitoIdentityPoolID = args.cognitoIdentityPoolID +topic = args.topic # Configure logging logger = logging.getLogger("AWSIoTPythonSDK.core") @@ -111,7 +69,7 @@ def customCallback(client, userdata, message): SessionToken = temporaryCredentials["Credentials"]["SessionToken"] # Init AWSIoTMQTTClient -myAWSIoTMQTTClient = AWSIoTMQTTClient("basicPubSub_CognitoSTS", useWebsocket=True) +myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId, useWebsocket=True) # AWSIoTMQTTClient configuration myAWSIoTMQTTClient.configureEndpoint(host, 443) @@ -125,12 +83,12 @@ def customCallback(client, userdata, message): # Connect and subscribe to AWS IoT myAWSIoTMQTTClient.connect() -myAWSIoTMQTTClient.subscribe("sdk/test/Python", 1, customCallback) +myAWSIoTMQTTClient.subscribe(topic, 1, customCallback) time.sleep(2) # Publish to the same topic in a loop forever loopCount = 0 while True: - myAWSIoTMQTTClient.publish("sdk/test/Python", "New Message " + str(loopCount), 1) + myAWSIoTMQTTClient.publish(topic, "New Message " + str(loopCount), 1) loopCount += 1 time.sleep(1) diff --git a/samples/basicShadow/basicShadowDeltaListener.py b/samples/basicShadow/basicShadowDeltaListener.py index efe7757..86d2b5c 100755 --- a/samples/basicShadow/basicShadowDeltaListener.py +++ b/samples/basicShadow/basicShadowDeltaListener.py @@ -20,7 +20,7 @@ import logging import time import json -import getopt +import argparse # Shadow JSON schema: # @@ -44,80 +44,32 @@ def customShadowCallback_Delta(payload, responseStatus, token): print("version: " + str(payloadDict["version"])) print("+++++++++++++++++++++++\n\n") -# Usage -usageInfo = """Usage: - -Use certificate based mutual authentication: -python basicShadowDeltaListener.py -e -r -c -k - -Use MQTT over WebSocket: -python basicShadowDeltaListener.py -e -r -w - -Type "python basicShadowDeltaListener.py -h" for available options. - - -""" -# Help info -helpInfo = """-e, --endpoint - Your AWS IoT custom endpoint --r, --rootCA - Root CA file path --c, --cert - Certificate file path --k, --key - Private key file path --w, --websocket - Use MQTT over WebSocket --h, --help - Help information - - -""" - # Read in command-line parameters -useWebsocket = False -host = "" -rootCAPath = "" -certificatePath = "" -privateKeyPath = "" -try: - opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"]) - if len(opts) == 0: - raise getopt.GetoptError("No input parameters!") - for opt, arg in opts: - if opt in ("-h", "--help"): - print(helpInfo) - exit(0) - if opt in ("-e", "--endpoint"): - host = arg - if opt in ("-r", "--rootCA"): - rootCAPath = arg - if opt in ("-c", "--cert"): - certificatePath = arg - if opt in ("-k", "--key"): - privateKeyPath = arg - if opt in ("-w", "--websocket"): - useWebsocket = True -except getopt.GetoptError: - print(usageInfo) - exit(1) +parser = argparse.ArgumentParser() +parser.add_argument("-e", "--endpoint", action="store", required=True, dest="host", help="Your AWS IoT custom endpoint") +parser.add_argument("-r", "--rootCA", action="store", required=True, dest="rootCAPath", help="Root CA file path") +parser.add_argument("-c", "--cert", action="store", dest="certificatePath", help="Certificate file path") +parser.add_argument("-k", "--key", action="store", dest="privateKeyPath", help="Private key file path") +parser.add_argument("-w", "--websocket", action="store_true", dest="useWebsocket", default=False, + help="Use MQTT over WebSocket") +parser.add_argument("-n", "--thingName", action="store", dest="thingName", default="Bot", help="Targeted thing name") +parser.add_argument("-id", "--clientId", action="store", dest="clientId", default="basicShadowDeltaListener", help="Targeted client id") + +args = parser.parse_args() +host = args.host +rootCAPath = args.rootCAPath +certificatePath = args.certificatePath +privateKeyPath = args.privateKeyPath +useWebsocket = args.useWebsocket +thingName = args.thingName +clientId = args.clientId + +if args.useWebsocket and args.certificatePath and args.privateKeyPath: + parser.error("X.509 cert authentication and WebSocket are mutual exclusive. Please pick one.") + exit(2) -# Missing configuration notification -missingConfiguration = False -if not host: - print("Missing '-e' or '--endpoint'") - missingConfiguration = True -if not rootCAPath: - print("Missing '-r' or '--rootCA'") - missingConfiguration = True -if not useWebsocket: - if not certificatePath: - print("Missing '-c' or '--cert'") - missingConfiguration = True - if not privateKeyPath: - print("Missing '-k' or '--key'") - missingConfiguration = True -if missingConfiguration: +if not args.useWebsocket and (not args.certificatePath or not args.privateKeyPath): + parser.error("Missing credentials for authentication.") exit(2) # Configure logging @@ -131,11 +83,11 @@ def customShadowCallback_Delta(payload, responseStatus, token): # Init AWSIoTMQTTShadowClient myAWSIoTMQTTShadowClient = None if useWebsocket: - myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener", useWebsocket=True) + myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId, useWebsocket=True) myAWSIoTMQTTShadowClient.configureEndpoint(host, 443) myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath) else: - myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener") + myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId) myAWSIoTMQTTShadowClient.configureEndpoint(host, 8883) myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath) @@ -148,11 +100,11 @@ def customShadowCallback_Delta(payload, responseStatus, token): myAWSIoTMQTTShadowClient.connect() # Create a deviceShadow with persistent subscription -Bot = myAWSIoTMQTTShadowClient.createShadowHandlerWithName("Bot", True) +deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True) # Listen on deltas -Bot.shadowRegisterDeltaCallback(customShadowCallback_Delta) +deviceShadowHandler.shadowRegisterDeltaCallback(customShadowCallback_Delta) # Loop forever while True: - pass + time.sleep(1) diff --git a/samples/basicShadow/basicShadowUpdater.py b/samples/basicShadow/basicShadowUpdater.py index 862e8dc..8b7d39f 100755 --- a/samples/basicShadow/basicShadowUpdater.py +++ b/samples/basicShadow/basicShadowUpdater.py @@ -20,7 +20,8 @@ import logging import time import json -import getopt +import argparse + # Shadow JSON schema: # @@ -31,108 +32,60 @@ # "property": # } # } -#} +# } # Custom Shadow callback def customShadowCallback_Update(payload, responseStatus, token): - # payload is a JSON string ready to be parsed using json.loads(...) - # in both Py2.x and Py3.x - if responseStatus == "timeout": - print("Update request " + token + " time out!") - if responseStatus == "accepted": - payloadDict = json.loads(payload) - print("~~~~~~~~~~~~~~~~~~~~~~~") - print("Update request with token: " + token + " accepted!") - print("property: " + str(payloadDict["state"]["desired"]["property"])) - print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") - if responseStatus == "rejected": - print("Update request " + token + " rejected!") + # payload is a JSON string ready to be parsed using json.loads(...) + # in both Py2.x and Py3.x + if responseStatus == "timeout": + print("Update request " + token + " time out!") + if responseStatus == "accepted": + payloadDict = json.loads(payload) + print("~~~~~~~~~~~~~~~~~~~~~~~") + print("Update request with token: " + token + " accepted!") + print("property: " + str(payloadDict["state"]["desired"]["property"])) + print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") + if responseStatus == "rejected": + print("Update request " + token + " rejected!") def customShadowCallback_Delete(payload, responseStatus, token): - if responseStatus == "timeout": - print("Delete request " + token + " time out!") - if responseStatus == "accepted": - print("~~~~~~~~~~~~~~~~~~~~~~~") - print("Delete request with token: " + token + " accepted!") - print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") - if responseStatus == "rejected": - print("Delete request " + token + " rejected!") - -# Usage -usageInfo = """Usage: - -Use certificate based mutual authentication: -python basicShadowUpdater.py -e -r -c -k - -Use MQTT over WebSocket: -python basicShadowUpdater.py -e -r -w - -Type "python basicShadowUpdater.py -h" for available options. - - -""" -# Help info -helpInfo = """-e, --endpoint - Your AWS IoT custom endpoint --r, --rootCA - Root CA file path --c, --cert - Certificate file path --k, --key - Private key file path --w, --websocket - Use MQTT over WebSocket --h, --help - Help information - - -""" + if responseStatus == "timeout": + print("Delete request " + token + " time out!") + if responseStatus == "accepted": + print("~~~~~~~~~~~~~~~~~~~~~~~") + print("Delete request with token: " + token + " accepted!") + print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") + if responseStatus == "rejected": + print("Delete request " + token + " rejected!") # Read in command-line parameters -useWebsocket = False -host = "" -rootCAPath = "" -certificatePath = "" -privateKeyPath = "" -try: - opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"]) - if len(opts) == 0: - raise getopt.GetoptError("No input parameters!") - for opt, arg in opts: - if opt in ("-h", "--help"): - print(helpInfo) - exit(0) - if opt in ("-e", "--endpoint"): - host = arg - if opt in ("-r", "--rootCA"): - rootCAPath = arg - if opt in ("-c", "--cert"): - certificatePath = arg - if opt in ("-k", "--key"): - privateKeyPath = arg - if opt in ("-w", "--websocket"): - useWebsocket = True -except getopt.GetoptError: - print(usageInfo) - exit(1) - -# Missing configuration notification -missingConfiguration = False -if not host: - print("Missing '-e' or '--endpoint'") - missingConfiguration = True -if not rootCAPath: - print("Missing '-r' or '--rootCA'") - missingConfiguration = True -if not useWebsocket: - if not certificatePath: - print("Missing '-c' or '--cert'") - missingConfiguration = True - if not privateKeyPath: - print("Missing '-k' or '--key'") - missingConfiguration = True -if missingConfiguration: - exit(2) +parser = argparse.ArgumentParser() +parser.add_argument("-e", "--endpoint", action="store", required=True, dest="host", help="Your AWS IoT custom endpoint") +parser.add_argument("-r", "--rootCA", action="store", required=True, dest="rootCAPath", help="Root CA file path") +parser.add_argument("-c", "--cert", action="store", dest="certificatePath", help="Certificate file path") +parser.add_argument("-k", "--key", action="store", dest="privateKeyPath", help="Private key file path") +parser.add_argument("-w", "--websocket", action="store_true", dest="useWebsocket", default=False, + help="Use MQTT over WebSocket") +parser.add_argument("-n", "--thingName", action="store", dest="thingName", default="Bot", help="Targeted thing name") +parser.add_argument("-id", "--clientId", action="store", dest="clientId", default="basicShadowUpdater", help="Targeted client id") + +args = parser.parse_args() +host = args.host +rootCAPath = args.rootCAPath +certificatePath = args.certificatePath +privateKeyPath = args.privateKeyPath +useWebsocket = args.useWebsocket +thingName = args.thingName +clientId = args.clientId + +if args.useWebsocket and args.certificatePath and args.privateKeyPath: + parser.error("X.509 cert authentication and WebSocket are mutual exclusive. Please pick one.") + exit(2) + +if not args.useWebsocket and (not args.certificatePath or not args.privateKeyPath): + parser.error("Missing credentials for authentication.") + exit(2) # Configure logging logger = logging.getLogger("AWSIoTPythonSDK.core") @@ -145,13 +98,13 @@ def customShadowCallback_Delete(payload, responseStatus, token): # Init AWSIoTMQTTShadowClient myAWSIoTMQTTShadowClient = None if useWebsocket: - myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowUpdater", useWebsocket=True) - myAWSIoTMQTTShadowClient.configureEndpoint(host, 443) - myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath) + myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId, useWebsocket=True) + myAWSIoTMQTTShadowClient.configureEndpoint(host, 443) + myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath) else: - myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowUpdater") - myAWSIoTMQTTShadowClient.configureEndpoint(host, 8883) - myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath) + myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId) + myAWSIoTMQTTShadowClient.configureEndpoint(host, 8883) + myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath) # AWSIoTMQTTShadowClient configuration myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20) @@ -162,15 +115,15 @@ def customShadowCallback_Delete(payload, responseStatus, token): myAWSIoTMQTTShadowClient.connect() # Create a deviceShadow with persistent subscription -Bot = myAWSIoTMQTTShadowClient.createShadowHandlerWithName("Bot", True) +deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True) # Delete shadow JSON doc -Bot.shadowDelete(customShadowCallback_Delete, 5) +deviceShadowHandler.shadowDelete(customShadowCallback_Delete, 5) # Update shadow in a loop loopCount = 0 while True: - JSONPayload = '{"state":{"desired":{"property":' + str(loopCount) + '}}}' - Bot.shadowUpdate(JSONPayload, customShadowCallback_Update, 5) - loopCount += 1 - time.sleep(1) + JSONPayload = '{"state":{"desired":{"property":' + str(loopCount) + '}}}' + deviceShadowHandler.shadowUpdate(JSONPayload, customShadowCallback_Update, 5) + loopCount += 1 + time.sleep(1)