class MQTTClient:
    def __init__(self, device_id, cert, key):
        # For certificate based connection
        self.device_id = str(device_id)
        self.state = 0
        self.client = AWSIoTMQTTClient(self.device_id)
        #TODO 2: modify your broker address
        self.client.configureEndpoint(
            "a191olynvpydfg-ats.iot.us-west-2.amazonaws.com", 8883)
        self.client.configureCredentials("root-ca-cert.pem", key, cert)
        self.client.configureOfflinePublishQueueing(
            -1)  # Infinite offline Publish queueing
        self.client.configureDrainingFrequency(2)  # Draining: 2 Hz
        self.client.configureConnectDisconnectTimeout(10)  # 10 sec
        self.client.configureMQTTOperationTimeout(5)  # 5 sec
        self.client.onMessage = self.customOnMessage

    def customOnMessage(self, message):
        #TODO3: fill in the function to show your received message
        print("client {} received -".format(self.device_id))  #end = "\n")

        #Don't delete this line
        self.client.disconnectAsync()

    # Suback callback
    def customSubackCallback(self, mid, data):
        #You don't need to write anything here
        pass

    # Puback callback
    def customPubackCallback(self, mid):
        #You don't need to write anything here
        pass

    def publish(self):
        #TODO4: fill in this function for your publish
        self.client.connect()
        self.client.subscribeAsync(self.device_id,
                                   1,
                                   ackCallback=self.customSubackCallback)

        index = np.random.randint(0, len(data[self.state]))
        payload = {
            "device_id": self.device_id,
            "class": str(self.state),
            "features": list(data[self.state].iloc[index].values)
        }
        self.client.publishAsync("data/heartbeat",
                                 json.dumps(payload),
                                 0,
                                 ackCallback=self.customPubackCallback)
Beispiel #2
0
class MQTTClient:
    def __init__(self, device_id, cert, key):
        self.device_id = str(device_id)
        self.state = 0
        self.client = AWSIoTMQTTClient(self.device_id)
        self.client.configureEndpoint(
            "a191olynvpydfg-ats.iot.us-west-2.amazonaws.com", 8883)
        self.client.configureCredentials("root-ca-cert.pem", key, cert)
        self.client.configureOfflinePublishQueueing(
            -1)  # Infinite offline Publish queueing
        self.client.configureDrainingFrequency(2)  # Draining: 2 Hz
        self.client.configureConnectDisconnectTimeout(10)  # 10 sec
        self.client.configureMQTTOperationTimeout(5)  # 5 sec
        self.client.onMessage = self.customOnMessage

    def customOnMessage(self, message):
        print("client {} received prediction {} \n".format(
            self.device_id,
            json.loads(message.payload)["prediction"]))
        self.client.disconnectAsync()

    # Suback callback
    def customSubackCallback(self, mid, data):
        pass

    # Puback callback
    def customPubackCallback(self, mid):
        pass

    def publish(self):
        self.client.connect()
        self.client.subscribeAsync(self.device_id,
                                   1,
                                   ackCallback=self.customSubackCallback)

        index = np.random.randint(0, len(data[self.state]))
        payload = {
            "device_id": self.device_id,
            "class": str(self.state),
            "features": list(data[self.state].iloc[index].values)
        }
        self.client.publishAsync("data/heartbeat",
                                 json.dumps(payload),
                                 0,
                                 ackCallback=self.customPubackCallback)
class MQTTClient:
    def __init__(self, device_id, cert, key):
        # For certificate based connection
        self.device_id = str(device_id)
        self.state = 0
        self.client = AWSIoTMQTTClient(self.device_id)
        #TODO 2: modify your broker address
        self.client.configureEndpoint("Your broker address", 8883)
        self.client.configureCredentials("./AmazonRootCA1.pem", key, cert)
        self.client.configureOfflinePublishQueueing(
            -1)  # Infinite offline Publish queueing
        self.client.configureDrainingFrequency(2)  # Draining: 2 Hz
        self.client.configureConnectDisconnectTimeout(10)  # 10 sec
        self.client.configureMQTTOperationTimeout(5)  # 5 sec
        self.client.onMessage = self.customOnMessage

    def customOnMessage(self, message):
        #TODO3: fill in the function to show your received message
        print("client {} received -".format(self.device_id), end=" ")

        #Don't delete this line
        self.client.disconnectAsync()

    # Suback callback
    def customSubackCallback(self, mid, data):
        #You don't need to write anything here
        pass

    # Puback callback
    def customPubackCallback(self, mid):
        #You don't need to write anything here
        pass

    def publish(self, payload):
        #TODO4: fill in this function for your publish
        self.client.connect()
        self.client.subscribeAsync("hearPred/" + self.device_id,
                                   0,
                                   ackCallback=self.customSubackCallback)

        self.client.publishAsync("data/" + self.device_id,
                                 payload,
                                 0,
                                 ackCallback=self.customPubackCallback)
Beispiel #4
0
class MQTTClient:
	def __init__(self, device_id, cert, key):
		# For certificate based connection
		self.device_id = str(device_id)
		self.state = 0
		self.client = AWSIoTMQTTClient(self.device_id)
		self.client.configureEndpoint(my_iotEndpoint, 8883)
		self.client.configureCredentials(rootCApath, key, cert)
		self.client.configureOfflinePublishQueueing(-1)  # Infinite offline Publish queueing
		self.client.configureDrainingFrequency(2)  # Draining: 2 Hz
		self.client.configureConnectDisconnectTimeout(10)  # 10 sec
		self.client.configureMQTTOperationTimeout(5)  # 5 sec
		self.client.onMessage = self.customOnMessage
		

	def customOnMessage(self,message):
		print("client {} received from topic {} - payload {}\n".format(
			self.device_id, message.topic, message.payload.decode()), end = " ")

		#Don't delete this line
		self.client.disconnectAsync()


	# Suback callback
	def customSubackCallback(self,mid, data):
		#You don't need to write anything here
	    pass


	# Puback callback
	def customPubackCallback(self,mid):
		#You don't need to write anything here
	    pass


	def publish(self):
		#TODO4: fill in this function for your publish
		# QoS: 0 -> <= 1  1 -> >= 1
		qosLevel = 0
		self.client.connect()
		# creates topic string per device, such as `CS498/wearable/17`
		topicTuple = (thingGroup, thingType, self.device_id)
		topicString = '/'.join(topicTuple)

		# load a sample values from corresponding data/csv depending on state
		json_data = {}
		json_data['category'] = ecgDataLabels[self.state]

		dataframe = data[self.state]
		json_data['readings'] = dataframe.sample().to_json()
		payload = json.dumps(json_data)

		#optional debug
		print('Device {} Topic: {}'.format(self.device_id,topicString))
		# print('Device {} Payload: {}'.format(self.device_id,payload))

		# subscribe
		self.client.subscribeAsync(topicString, qosLevel, ackCallback=self.customSubackCallback)
		# publish
		if self.state>0:
			self.client.publishAsync(topicString, payload, qosLevel, ackCallback=self.customPubackCallback)
Beispiel #5
0
class ProvisioningHandler:
    def __init__(self, file_path):
        """Initializes the provisioning handler
        
        Arguments:
            file_path {string} -- path to your configuration file
        """
        #Logging
        logging.basicConfig(level=logging.ERROR)
        self.logger = logging.getLogger(__name__)

        #Load configuration settings from config.ini
        config = Config(file_path)
        self.config_parameters = config.get_section('SETTINGS')
        self.secure_cert_path = self.config_parameters['SECURE_CERT_PATH']
        self.iot_endpoint = self.config_parameters['IOT_ENDPOINT']
        self.template_name = self.config_parameters['PRODUCTION_TEMPLATE']
        self.rotation_template = self.config_parameters[
            'CERT_ROTATION_TEMPLATE']
        self.claim_cert = self.config_parameters['CLAIM_CERT']
        self.secure_key = self.config_parameters['SECURE_KEY']
        self.root_cert = self.config_parameters['ROOT_CERT']
        self.machine_config = self.config_parameters['MACHINE_CONFIG_PATH']

        with open(self.machine_config) as json_file:
            data = json.load(json_file)
            self.serial_num = data['serial_num']
            self.model_type = data['model_type']

        self.unique_id = self.serial_num

        # ------------------------------------------------------------------------------
        #  -- PROVISIONING HOOKS EXAMPLE --
        # Provisioning Hooks are a powerful feature for fleet provisioning. Most of the
        # heavy lifting is performed within the cloud lambda. However, you can send
        # device attributes to be validated by the lambda. An example is show in the line
        # below (.hasValidAccount could be checked in the cloud against a database).
        # Alternatively, a serial number, geo-location, or any attribute could be sent.
        #
        # -- Note: This attribute is passed up as part of the register_thing method and
        # will be validated in your lambda's event data.
        # ------------------------------------------------------------------------------

        self.primary_MQTTClient = AWSIoTMQTTClient(self.unique_id)
        self.test_MQTTClient = AWSIoTMQTTClient(self.unique_id)
        self.primary_MQTTClient.onMessage = self.on_message_callback
        self.callback_returned = False
        self.message_payload = {}
        self.isRotation = False

    def core_connect(self):
        """ Method used to connect to connect to AWS IoTCore Service. Endpoint collected from config.
        
        """
        if self.isRotation:
            self.logger.info('##### CONNECTING WITH EXISTING CERT #####')
            print('##### CONNECTING WITH EXISTING CERT #####')
            self.get_current_certs()
        else:
            self.logger.info(
                '##### CONNECTING WITH PROVISIONING CLAIM CERT #####')
            print('##### CONNECTING WITH PROVISIONING CLAIM CERT #####')

        self.primary_MQTTClient.configureEndpoint(self.iot_endpoint, 8883)
        self.primary_MQTTClient.configureCredentials(
            "{}/{}".format(self.secure_cert_path, self.root_cert),
            "{}/{}".format(self.secure_cert_path, self.secure_key),
            "{}/{}".format(self.secure_cert_path, self.claim_cert))
        self.primary_MQTTClient.configureOfflinePublishQueueing(-1)
        self.primary_MQTTClient.configureDrainingFrequency(2)
        self.primary_MQTTClient.configureConnectDisconnectTimeout(10)
        self.primary_MQTTClient.configureMQTTOperationTimeout(3)

        self.primary_MQTTClient.connect()

    def get_current_certs(self):
        """ Gets the currently active production certificates for a device.
        """
        non_bootstrap_certs = glob.glob('{}/[!boot]*.crt'.format(
            self.secure_cert_path))
        non_bootstrap_key = glob.glob('{}/[!boot]*.key'.format(
            self.secure_cert_path))

        #Get the current cert
        if len(non_bootstrap_certs) > 0:
            self.claim_cert = os.path.basename(non_bootstrap_certs[0])

        #Get the current key
        if len(non_bootstrap_key) > 0:
            self.secure_key = os.path.basename(non_bootstrap_key[0])

    def enable_error_monitor(self):
        """ Subscribe to pertinent IoTCore topics that would emit errors
        """
        self.primary_MQTTClient.subscribe(
            "$aws/provisioning-templates/{}/provision/json/rejected".format(
                self.template_name),
            1,
            callback=self.basic_callback)
        self.primary_MQTTClient.subscribe(
            "$aws/certificates/create/json/rejected",
            1,
            callback=self.basic_callback)

    def get_official_certs(self, callback, isRotation=False):
        """ Initiates an async loop/call to kick off the provisioning flow.

            Triggers:
               on_message_callback() providing the certificate payload
        """
        if isRotation:
            self.template_name = self.rotation_template
            self.isRotation = True

        return asyncio.run(self.orchestrate_provisioning_flow(callback))

    async def orchestrate_provisioning_flow(self, callback):
        # Connect to core with provision claim creds
        try:
            self.core_connect()
        except ssl.SSLError:
            print(
                "Duplicate prod certs exist in your cert directory. Remove any certs not associated with device."
            )
            sys.exit()

        # Monitor topics for errors
        self.enable_error_monitor()

        # Make a publish call to topic to get official certs
        self.primary_MQTTClient.publish("$aws/certificates/create/json", "{}",
                                        0)

        # Wait the function return until all callbacks have returned
        # Returned denoted when callback flag is set in this class.
        while not self.callback_returned:
            await asyncio.sleep(0)

        return callback(self.message_payload)

    def on_message_callback(self, message):
        """ Callback Message handler responsible for workflow routing of msg responses from provisioning services.
        
        Arguments:
            message {string} -- The response message payload.
        """
        json_data = json.loads(message.payload)

        # A response has been recieved from the service that contains certificate data.
        if 'certificateId' in json_data:
            self.logger.info('##### SUCCESS. SAVING KEYS TO DEVICE! #####')
            print('##### SUCCESS. SAVING KEYS TO DEVICE! #####')
            self.assemble_certificates(json_data)

        # A response contains acknowledgement that the provisioning template has been acted upon.
        elif 'deviceConfiguration' in json_data:
            if self.isRotation:
                self.logger.info('##### ACTIVATION COMPLETE #####')
                print('##### ACTIVATION COMPLETE #####')
            else:
                self.logger.info(
                    '##### CERT ACTIVATED AND THING {} CREATED #####'.format(
                        json_data['thingName']))
                print('##### CERT ACTIVATED AND THING {} CREATED #####'.format(
                    json_data['thingName']))

            self.validate_certs()
        elif 'statusCode' in json_data:
            if json_data['statusCode'] == 403:
                os.remove("{}/{}".format(self.secure_cert_path,
                                         self.new_key_name))
                os.remove("{}/{}".format(self.secure_cert_path,
                                         self.new_cert_name))
        else:
            self.logger.info(json_data)

    def assemble_certificates(self, payload):
        """ Method takes the payload and constructs/saves the certificate and private key. Method uses
        existing AWS IoT Core naming convention.
        
        Arguments:
            payload {string} -- Certifiable certificate/key data.

        Returns:
            ownership_token {string} -- proof of ownership from certificate issuance activity.
        """
        ### Cert ID
        cert_id = payload['certificateId']
        self.new_key_root = cert_id[0:10]

        self.new_cert_name = '{}-certificate.pem.crt'.format(self.new_key_root)
        ### Create certificate
        f = open('{}/{}'.format(self.secure_cert_path, self.new_cert_name),
                 'w+')
        f.write(payload['certificatePem'])
        f.close()

        ### Create private key
        self.new_key_name = '{}-private.pem.key'.format(self.new_key_root)
        f = open('{}/{}'.format(self.secure_cert_path, self.new_key_name),
                 'w+')
        f.write(payload['privateKey'])
        f.close()

        ### Extract/return Ownership token
        self.ownership_token = payload['certificateOwnershipToken']

        #register newly aquired cert
        self.register_thing(self.unique_id, self.ownership_token)

    def register_thing(self, serial, token):
        """Calls the fleet provisioning service responsible for acting upon instructions within device templates.
        
        Arguments:
            serial {string} -- unique identifer for the thing. Specified as a property in provisioning template.
            token {string} -- The token response from certificate creation to prove ownership/immediate possession of the certs.
            
        Triggers:
            on_message_callback() - providing acknowledgement that the provisioning template was processed.
        """
        if self.isRotation:
            self.logger.info('##### VALIDATING EXPIRY & ACTIVATING CERT #####')
            print('##### VALIDATING EXPIRY & ACTIVATING CERT #####')
        else:
            self.logger.info('##### CREATING THING ACTIVATING CERT #####')
            print('##### CREATING THING ACTIVATING CERT #####')

        register_template = {
            "certificateOwnershipToken": token,
            "parameters": {
                "DeviceSerial": serial
            }
        }

        #Register thing / activate certificate
        self.primary_MQTTClient.publish(
            "$aws/provisioning-templates/{}/provision/json".format(
                self.template_name), json.dumps(register_template), 0)

    def validate_certs(self):
        """Responsible for (re)connecting to IoTCore with the newly provisioned/activated certificate - (first class citizen cert)
        """
        self.logger.info('##### CONNECTING WITH OFFICIAL CERT #####')
        print('##### CONNECTING WITH OFFICIAL CERT #####')
        self.cert_validation_test()
        self.new_cert_pub_sub()
        print("##### ACTIVATED AND TESTED CREDENTIALS ({}, {}). #####".format(
            self.new_key_name, self.new_cert_name))
        print("##### FILES SAVED TO {} #####".format(self.secure_cert_path))

    def cert_validation_test(self):
        self.primary_MQTTClient.disconnectAsync()
        self.test_MQTTClient.configureEndpoint(self.iot_endpoint, 8883)
        self.test_MQTTClient.configureCredentials(
            "{}/{}".format(self.secure_cert_path, self.root_cert),
            "{}/{}".format(self.secure_cert_path, self.new_key_name),
            "{}/{}".format(self.secure_cert_path, self.new_cert_name))
        self.test_MQTTClient.configureOfflinePublishQueueing(
            -1)  # Infinite offline Publish queueing
        self.test_MQTTClient.configureDrainingFrequency(2)  # Draining: 2 Hz
        self.test_MQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
        self.test_MQTTClient.configureMQTTOperationTimeout(3)  # 5 sec
        self.test_MQTTClient.connect()

    def basic_callback(self, client, userdata, msg):
        """Method responding to the openworld publish attempt. Demonstrating a successful pub/sub with new certificate.
        """
        self.logger.info(msg.payload.decode())
        self.message_payload = msg.payload.decode()
        self.callback_returned = True

    def new_cert_pub_sub(self):
        """Method testing a call to the 'openworld' topic (which was specified in the policy for the new certificate)
        """
        self.test_MQTTClient.subscribe("cmd/{}/alerts".format(self.unique_id),
                                       1, self.basic_callback)
        self.test_MQTTClient.publish(
            "cmd/{}/alerts".format(self.unique_id),
            str({
                "service_response":
                "##### RESPONSE FROM PREVIOUSLY FORBIDDEN TOPIC #####"
            }), 0)