def __init__(self, Log, Id, InFileName, ModelType, NumberOfDevices):

        self.logger = Log
        self.id_device = Id
        self.in_file_name = InFileName
        self.model_type = ModelType
        self.number_of_devices = NumberOfDevices

        # Load the configuration file
        self.config = {}
        self.load_config()

        # Symmetric Key
        self.symmetrickey = SymmetricKey(self.logger)

        # Secrets
        self.secrets = Secrets(self.logger)
        self.secrets_cache_data = self.secrets.data

        # meta
        self.application_uri = None
        self.namespace = None
        self.device_capability_model_id = None
        self.device_capability_model = []
        self.device_name_prefix = None
        self.ignore_interface_ids = []

        # Devices Cache
        self.devices_cache = DevicesCache(self.logger)
        self.devices_cache_data = self.devices_cache.data
        self.devices_to_provision = []
Exemplo n.º 2
0
    def __init__(self, Log):

        # Initialization
        self.logger = Log
        self.id_device = None

        # Load the configuration file
        self.config = Config(self.logger)
        self.config = self.config.data

        # Logging Mappers
        data = [
            x for x in self.config["ClassLoggingMaps"]
            if x["Name"] == "ProvisionDevice"
        ]
        self.class_name_map = data[0]["LoggingId"]

        # Symmetric Key
        self.symmetrickey = SymmetricKey(self.logger)

        # Secrets Cache
        self.secrets = None
        self.secrets_cache_data = []

        # Devices Cache
        self.devices_cache = None
        self.devices_cache_data = []

        # meta
        self.application_uri = None
        self.namespace = None
        self.device_name = None
        self.device_default_component_id = None
        self.device_capability_model = []
        self.device_name_prefix = None
        self.ignore_interface_ids = []
        self.device_to_provision = None
        self.device_to_provision_array = []
Exemplo n.º 3
0
class ProvisionDevices():

    timer = None
    timer_ran = False
    dcm_value = None

    def __init__(self, Log):

        # Initialization
        self.logger = Log
        self.id_device = None

        # Load the configuration file
        self.config = Config(self.logger)
        self.config = self.config.data

        # Logging Mappers
        data = [
            x for x in self.config["ClassLoggingMaps"]
            if x["Name"] == "ProvisionDevice"
        ]
        self.class_name_map = data[0]["LoggingId"]

        # Symmetric Key
        self.symmetrickey = SymmetricKey(self.logger)

        # Secrets Cache
        self.secrets = None
        self.secrets_cache_data = []

        # Devices Cache
        self.devices_cache = None
        self.devices_cache_data = []

        # meta
        self.application_uri = None
        self.namespace = None
        self.device_name = None
        self.device_default_component_id = None
        self.device_capability_model = []
        self.device_name_prefix = None
        self.ignore_interface_ids = []
        self.device_to_provision = None
        self.device_to_provision_array = []

    # -------------------------------------------------------------------------------
    #   Function:   provision_devices
    #   Usage:      Grabs the Defined Devices and Provisions into IoT Central
    #               a provisioning call to associated a device template to the node
    #               interface based on the twin, device or gateway pattern
    # -------------------------------------------------------------------------------
    async def provision_devices(self, Id):

        # First up we gather all of the needed provisioning meta-data and secrets
        try:

            self.id_device = Id
            self.namespace = self.config["Device"]["NameSpace"]
            self.device_default_component_id = self.config["Device"][
                "DefaultComponentId"]
            self.device_name_prefix = self.config["Device"]["DeviceNamePrefix"]
            self.device_name = self.device_name_prefix.format(
                id=self.id_device)
            self.device_default_component_id = self.config["Device"][
                "DefaultComponentId"]

            # Load all our cache data
            self.load_caches()

            # this is our working device for things we provision in this session
            self.device_to_provision = self.create_device_to_provision()

            self.logger.info(
                "************************************************")
            self.logger.info("[%s] DEVICE TO PROVISION" % self.class_name_map)
            self.logger.info(pprint.pformat(self.device_to_provision))
            self.logger.info(
                "************************************************")

            # Azure IoT Central SDK Call to create the provisioning_device_client
            provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
                provisioning_host=self.secrets.get_provisioning_host(),
                registration_id=self.device_to_provision["Device"]["Name"],
                id_scope=self.secrets.get_scope_id(),
                symmetric_key=self.device_to_provision["Device"]["Secrets"]
                ["DeviceSymmetricKey"],
                websockets=True)

            # Azure IoT Central SDK call to set the payload and provision the device
            provisioning_device_client.provisioning_payload = '{"iotcModelId":"%s"}' % (
                self.device_to_provision["Device"]["DefaultComponentId"])
            registration_result = await provisioning_device_client.register()
            self.logger.info("[%s] RESULT %s" %
                             (self.class_name_map, registration_result))
            self.logger.info(
                "[%s] DEVICE SYMMETRIC KEY %s" %
                (self.class_name_map, self.device_to_provision["Device"]
                 ["Secrets"]["DeviceSymmetricKey"]))
            self.device_to_provision["Device"]["Secrets"][
                "AssignedHub"] = registration_result.registration_state.assigned_hub

            # Add Capabilities/Interfaces
            #for node in self.config["Nodes"]:
            #self.device_to_provision["Device"]["Capabilities"].append(node["InterfaceInstanceName"])

            # Update Secrets Cache Data for Devices
            existing_device = [
                x for x in self.secrets_cache_data["Devices"] if x["Device"]
                ["Name"] == self.device_to_provision["Device"]["Name"]
            ]

            if len(existing_device) == 0:
                self.secrets_cache_data["Devices"].append(
                    self.device_to_provision)
            else:
                index = 0
                for device in self.secrets_cache_data["Devices"]:
                    if device["Device"]["Name"] == self.device_to_provision[
                            "Device"]["Name"]:
                        self.secrets_cache_data["Devices"][index][
                            "Device"] = self.device_to_provision["Device"]
                        break
                    else:
                        index = index + 1

            # Update Full Device Information to the Secrets file.
            # IMPORTANT: This hides the secrets in file in .gitignore
            self.secrets.update_file_device_secrets(
                self.secrets_cache_data["Devices"])

            # Hide secrets from device cache file
            self.device_to_provision["Device"]["Secrets"] = None
            existing_device = [
                x for x in self.devices_cache_data["Devices"] if x["Device"]
                ["Name"] == self.device_to_provision["Device"]["Name"]
            ]
            if len(existing_device) == 0:
                self.devices_cache_data["Devices"].append(
                    self.device_to_provision)

            index = 0
            for device in self.devices_cache_data["Devices"]:
                if device["Device"]["Name"] == self.device_to_provision[
                        "Device"]["Name"]:
                    self.devices_cache_data["Devices"][index][
                        "Device"] = self.device_to_provision["Device"]
                    break
                else:
                    index = index + 1

            self.devices_cache.update_file(self.devices_cache_data)

            self.logger.info(
                "************************************************")
            self.logger.info("[%s] SUCCESS:" % self.class_name_map)
            self.logger.info(pprint.pformat(self.device_to_provision))
            self.logger.info(
                "************************************************")

            return

        except Exception as ex:
            self.logger.error("[ERROR] %s" % ex)
            self.logger.error(
                "[TERMINATING] We encountered an error in provision_devices()")

    # -------------------------------------------------------------------------------
    #   Function:   create_device_to_provision`
    #   Usage:      Returns a Devices Array
    # -------------------------------------------------------------------------------
    def create_device_to_provision(self):

        newDeviceToProvision = {
            "Device": {
                "Name": self.device_name,
                "DefaultComponentId": self.device_default_component_id,
                "LastProvisioned": str(datetime.datetime.now()),
                "Secrets": {}
            }
        }
        print(self.secrets.get_device_primary_key())

        # Get device symmetric key
        device_symmetric_key = self.symmetrickey.compute_derived_symmetric_key(
            self.device_name, self.secrets.get_device_secondary_key())

        newDeviceSecret = {
            "Name": self.device_name,
            "DefaultComponentId": self.device_default_component_id,
            "AssignedHub": "",
            "DeviceSymmetricKey": device_symmetric_key,
            "LastProvisioned": str(datetime.datetime.now())
        }

        newDeviceToProvision["Device"]["Secrets"] = newDeviceSecret

        return newDeviceToProvision

    # -------------------------------------------------------------------------------
    #   Function:   create_device_capability_model
    #   Usage:      Returns a Device Interface with the  Interfaces Array
    # -------------------------------------------------------------------------------
    def create_device_capability_model(self):
        newDeviceCapabilityModel = {
            "Name": self.device_name,
            "DefaultComponentId": self.device_default_component_id,
            "LastProvisioned": str(datetime.datetime.now())
        }

        return newDeviceCapabilityModel

    # -------------------------------------------------------------------------------
    #   Function:   create_device_interface
    #   Usage:      Returns a Device Interface for Interfaces Array
    # -------------------------------------------------------------------------------
    def create_device_interface(self, name, Id, instantName):
        newInterface = {
            "Name": name,
            "InterfacelId": Id,
            "InterfaceInstanceName": instantName
        }

        return newInterface

    # -------------------------------------------------------------------------------
    #   Function:   load caches for provisioning
    #   Usage:      None
    # -------------------------------------------------------------------------------
    def load_caches(self):

        # Secrets Cache
        self.secrets = Secrets(self.logger)
        self.secrets_cache_data = self.secrets.data

        # Devices Cache
        self.devices_cache = DevicesCache(self.logger)
        self.devices_cache_data = self.devices_cache.data

        return
class ProvisionDevices():

    timer = None
    timer_ran = False
    dcm_value = None

    def __init__(self, Log, Id, InFileName, ModelType, NumberOfDevices):

        self.logger = Log
        self.id_device = Id
        self.in_file_name = InFileName
        self.model_type = ModelType
        self.number_of_devices = NumberOfDevices

        # Load the configuration file
        self.config = {}
        self.load_config()

        # Symmetric Key
        self.symmetrickey = SymmetricKey(self.logger)

        # Secrets
        self.secrets = Secrets(self.logger)
        self.secrets_cache_data = self.secrets.data

        # meta
        self.application_uri = None
        self.namespace = None
        self.device_capability_model_id = None
        self.device_capability_model = []
        self.device_name_prefix = None
        self.ignore_interface_ids = []

        # Devices Cache
        self.devices_cache = DevicesCache(self.logger)
        self.devices_cache_data = self.devices_cache.data
        self.devices_to_provision = []

    # -------------------------------------------------------------------------------
    #   Function:   provision_devices
    #   Usage:      Iterates through all of the nodes in config.json and will create
    #               a provisioning call to associated a device template to the node
    #               interface based on the twin, device or gateway pattern
    # -------------------------------------------------------------------------------
    async def provision_devices(self):

        # First up we gather all of the needed provisioning meta-data and secrets
        try:

            for pattern in self.config["IoTCentralPatterns"]:
                if pattern["ModelType"] == self.model_type:
                    self.namespace = pattern["NameSpace"]
                    self.device_capability_model_id = pattern[
                        "DeviceCapabilityModelId"]
                    self.device_name_prefix = pattern["DeviceNamePrefix"]
                    self.ignore_interface_ids = pattern["IgnoreInterfaceIds"]
                    break

            # this is our working cache for things we provision in this session
            self.devices_to_provision = self.create_devices_to_provision()

            # Specific string formatting based on the device-model-type
            if self.model_type == "Twins":
                self.twins_create()
            elif self.model_type == "Gateways":
                self.gateways_create()
            elif self.model_type == "Devices":
                self.devices_create()

            print("********************************")
            print("DEVICES TO PROVISION: %s" % self.devices_to_provision)
            print("********************************")

            # Update or Append new Records to the Devices Cache
            found_device = False
            for device_to_provision in self.devices_to_provision["Devices"][
                    "Devices"]:
                index = 0
                for devices_cache in self.devices_cache_data["Devices"]:
                    found_device = False
                    if devices_cache["Name"] == device_to_provision["Name"]:
                        self.devices_cache_data["Devices"][
                            index] = device_to_provision
                        found_device = True
                        break
                    else:
                        index = index + 1
                if found_device == False:
                    self.devices_cache_data["Devices"].append(
                        device_to_provision)

            # Update or Append new Records to the Secrets
            found_secret = False
            for device_to_provision in self.devices_to_provision["Secrets"]:

                # Azure IoT Central SDK Call to create the provisioning_device_client
                provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
                    provisioning_host=self.secrets.get_provisioning_host(),
                    registration_id=device_to_provision["Name"],
                    id_scope=self.secrets.get_scope_id(),
                    symmetric_key=device_to_provision["DeviceSymmetricKey"],
                    websockets=True)

                # Azure IoT Central SDK call to set the DCM payload and provision the device
                provisioning_device_client.provisioning_payload = '{"iotcModelId":"%s"}' % (
                    device_to_provision["DeviceCapabilityModelId"])
                registration_result = await provisioning_device_client.register(
                )
                self.logger.info("[REGISTRATION RESULT] %s" %
                                 registration_result)
                self.logger.info("[device_symmetrickey] %s" %
                                 device_to_provision["DeviceSymmetricKey"])
                device_to_provision[
                    "AssignedHub"] = registration_result.registration_state.assigned_hub

                index = 0
                for secrets_cache in self.secrets_cache_data["Devices"]:
                    found_secret = False
                    if secrets_cache["Name"] == device_to_provision["Name"]:
                        self.secrets_cache_data["Devices"][
                            index] = device_to_provision
                        found_secret = True
                        break
                    else:
                        index = index + 1
                if found_secret == False:
                    self.secrets_cache_data["Devices"].append(
                        device_to_provision)

            print("********************************")
            print("DEVICES TO PROVISION: %s" % self.devices_to_provision)
            print("********************************")

            self.devices_cache.update_file(self.devices_cache_data)
            self.secrets.update_file_device_secrets(
                self.secrets_cache_data["Devices"])
            return

        except Exception as ex:
            self.logger.error("[ERROR] %s" % ex)
            self.logger.error(
                "[TERMINATING] We encountered an error in CLASS::ProvisionDevices::provision_devices()"
            )

    # -------------------------------------------------------------------------------
    #   Function:   load_config
    #   Usage:      Loads the configuration
    # -------------------------------------------------------------------------------
    def load_config(self):

        config = Config(self.logger)
        self.config = config.data
        return

    # -------------------------------------------------------------------------------
    #   Function:   twins_create
    #   Usage:      Returns a a Twin pattern for Devices and Secrets
    # -------------------------------------------------------------------------------
    def twins_create(self):

        try:

            # We will iterate the number of devices we are going to create
            for x in range(self.number_of_devices):

                # Define the Device iteration suffix, it is
                # the base passed number with leading zeros for
                # a legnth of 3 (1-999) devices
                id_number_str = str(int(self.id_device) + x)
                id_number_str = id_number_str.zfill(3)

                device_name = self.device_name_prefix.format(id=id_number_str)

                # The Device Asset scenario appends one interfaces per device id
                device_capability_model = self.create_device_capability_model(
                    device_name, self.device_capability_model_id)

                # Let's Look at the config file and generate
                # our device from the interfaces configuration
                for node in self.config["Interfaces"]:

                    # check if we are excluding the interface?
                    if self.ignore_interface_ids.count(
                            node["InterfacelId"]) == 0:
                        device_capability_model["Interfaces"].append(
                            self.create_device_interface(
                                node["Name"], node["InterfacelId"],
                                node["InterfaceInstanceName"]))

                self.devices_to_provision["Devices"]["Devices"].append(
                    device_capability_model)
                self.devices_to_provision["Secrets"].append(
                    self.create_device_connection(
                        device_name, self.device_capability_model_id))

        except Exception as ex:
            self.logger.error("[ERROR] %s" % ex)
            self.logger.error(
                "[TERMINATING] We encountered an error in CLASS::ProvisionDevices::devices_create()"
            )

        return

    # -------------------------------------------------------------------------------
    #   Function:   gateways_create
    #   Usage:      Returns a a Gateway pattern for Devices and Secrets
    # -------------------------------------------------------------------------------
    def gateways_create(self):

        try:

            # Let's Look at the config file and generate
            # our device from the interfaces configuration
            for node in self.config["Nodes"]:

                device_capability_model_id = self.device_capability_model_id.format(
                    interfaceName=node["Name"])

                # check if we are excluding the interface?
                if self.ignore_interface_ids.count(node["InterfacelId"]) == 0:

                    # We will iterate the number of devices we are going to create
                    for x in range(self.number_of_devices):

                        # Define the Device iteration suffix, it is
                        # the base passed number with leading zeros for
                        # a legnth of 3 (1-999) devices
                        id_number_str = str(int(self.id_device) + x)
                        id_number_str = id_number_str.zfill(3)

                        device_name = self.device_name_prefix.format(
                            nodeName=node["Name"], id=id_number_str)

                        # The Device Asset scenario appends one interfaces per device id
                        device_capability_model = self.create_device_capability_model(
                            device_name, device_capability_model_id)
                        device_capability_model["Interfaces"].append(
                            self.create_device_interface(
                                node["Name"], node["InterfacelId"],
                                node["InterfaceInstanceName"]))

                        self.devices_to_provision["Devices"]["Devices"].append(
                            device_capability_model)
                        self.devices_to_provision["Secrets"].append(
                            self.create_device_connection(
                                device_name, device_capability_model_id))

        except Exception as ex:
            self.logger.error("[ERROR] %s" % ex)
            self.logger.error(
                "[TERMINATING] We encountered an error in CLASS::ProvisionDevices::devices_create()"
            )

        return

    # -------------------------------------------------------------------------------
    #   Function:   devices_create
    #   Usage:      Returns a Device Interface for Interfaces Array
    # -------------------------------------------------------------------------------
    def devices_create(self):

        try:

            # Let's Look at the config file and generate
            # our device from the interfaces configuration
            for node in self.config["Nodes"]:

                device_capability_model_id = self.device_capability_model_id.format(
                    interfaceName=node["Name"])

                # check if we are excluding the interface?
                if self.ignore_interface_ids.count(node["InterfacelId"]) == 0:

                    # We will iterate the number of devices we are going to create
                    for x in range(self.number_of_devices):

                        # Define the Device iteration suffix, it is
                        # the base passed number with leading zeros for
                        # a legnth of 3 (1-999) devices
                        id_number_str = str(int(self.id_device) + x)
                        id_number_str = id_number_str.zfill(3)

                        device_name = self.device_name_prefix.format(
                            nodeName=node["Name"], id=id_number_str)

                        # The Device Asset scenario appends one interfaces per device id
                        device_capability_model = self.create_device_capability_model(
                            device_name, device_capability_model_id)
                        device_capability_model["Interfaces"].append(
                            self.create_device_interface(
                                node["Name"], node["InterfacelId"],
                                node["InterfaceInstanceName"]))

                        self.devices_to_provision["Devices"]["Devices"].append(
                            device_capability_model)
                        self.devices_to_provision["Secrets"].append(
                            self.create_device_connection(
                                device_name, device_capability_model_id))

        except Exception as ex:
            self.logger.error("[ERROR] %s" % ex)
            self.logger.error(
                "[TERMINATING] We encountered an error in CLASS::ProvisionDevices::devices_create()"
            )

        return

    # -------------------------------------------------------------------------------
    #   Function:   create_devices_to_provision
    #   Usage:      Returns a Devices Array
    # -------------------------------------------------------------------------------
    def create_devices_to_provision(self):
        newDeviceToProvisionArray = {"Devices": {"Devices": []}, "Secrets": []}
        return newDeviceToProvisionArray

    # -------------------------------------------------------------------------------
    #   Function:   create_device_capability_model
    #   Usage:      Returns a Device Interface with the  Interfaces Array
    # -------------------------------------------------------------------------------
    def create_device_capability_model(self, DeviceName,
                                       DeviceCapabilityModelId):
        newDeviceCapabilityModel = {
            "Name": DeviceName,
            "DeviceType": self.model_type,
            "DeviceCapabilityModelId": DeviceCapabilityModelId,
            "Interfaces": [],
            "LastProvisioned": str(datetime.datetime.now())
        }
        return newDeviceCapabilityModel

    # -------------------------------------------------------------------------------
    #   Function:   create_device_interface
    #   Usage:      Returns a Device Interface for Interfaces Array
    # -------------------------------------------------------------------------------
    def create_device_interface(self, name, Id, instantName):
        newInterface = {
            "Name": name,
            "InterfacelId": Id,
            "InterfaceInstanceName": instantName
        }
        return newInterface

    # -------------------------------------------------------------------------------
    #   Function:   create_device_connection
    #   Usage:      Returns a Device Interface for Interfaces Array
    # -------------------------------------------------------------------------------
    def create_device_connection(self, Name, DeviceCapabilityModelId):

        # Get device symmetric key
        device_symmetric_key = self.symmetrickey.compute_derived_symmetric_key(
            Name, self.secrets.get_device_secondary_key())

        newDeviceSecret = {
            "Name": Name,
            "DeviceCapabilityModelId": DeviceCapabilityModelId,
            "DeviceType": self.model_type,
            "AssignedHub": "",
            "DeviceSymmetricKey": device_symmetric_key,
            "LastProvisioned": str(datetime.datetime.now())
        }
        return newDeviceSecret
    async def provision_devices(self):

        # Load the Devices Cache File for any devices
        # that have already been provisioned
        devicescache = DevicesCache(self.logger)
        self.logger.info("[DEVICES] devicescache.data Count %s" %
                         str(len(devicescache.data["Devices"])))

        # Make a working copy of the cache file
        self.data = devicescache.data
        self.data["Devices"] = [
            x for x in devicescache.data["Devices"]
            if x["DeviceName"] == "Simulated Device"
        ]
        self.logger.info("[DEVICES] self.data Count %s" %
                         str(len(self.data["Devices"])))
        devicescache.load_file()
        self.devices_provision = devicescache.data
        self.devices_provision["Devices"] = [
            x for x in devicescache.data["Devices"]
            if x["DeviceName"] != "Simulated Device"
        ]
        self.logger.info("[DEVICES] self.devices_provision.data Count %s" %
                         str(len(self.devices_provision["Devices"])))

        # secrets
        scope_id = None
        device_primary_key = None
        device_secondary_key = None
        gateway_primary_key = None
        gateway_secondary_key = None

        # load the secrets
        secrets = Secrets(self.logger)
        if secrets.data["KeyVaultSecrets"]:
            self.logger.info("[USING KEY VAULT SECRETS]")

            # key vault account uri
            key_vault_uri = secrets.data["KeyVaultSecrets"]["KeyVaultUri"]
            self.logger.info("[KEY VAULT URI] %s" % key_vault_uri)

            tenant_id = secrets.data["KeyVaultSecrets"]["TenantId"]
            client_id = secrets.data["KeyVaultSecrets"]["ClientId"]
            client_secret = secrets.data["KeyVaultSecrets"]["ClientSecret"]

            # Get access to Key Vault Secrets
            credential = ClientSecretCredential(tenant_id, client_id,
                                                client_secret)
            self.logger.info("[credential] %s" % credential)
            secret_client = SecretClient(vault_url=key_vault_uri,
                                         credential=credential)
            self.logger.info("[secret_client] %s" % secret_client)

            # Read all of our Secrets for Accessing IoT Central
            scope_id = secret_client.get_secret(
                secrets.data["KeyVaultSecrets"]["ScopeId"])
            device_primary_key = secret_client.get_secret(
                secrets.data["KeyVaultSecrets"]["DeviceConnect"]["SaSKeys"]
                ["Primary"])
            device_secondary_key = secret_client.get_secret(
                secrets.data["KeyVaultSecrets"]["DeviceConnect"]["SaSKeys"]
                ["Secondary"])
            gateway_primary_key = secret_client.get_secret(
                secrets.data["KeyVaultSecrets"]["GatewayConnect"]["SaSKeys"]
                ["Primary"])
            gateway_secondary_key = secret_client.get_secret(
                secrets.data["KeyVaultSecrets"]["GatewayConnect"]["SaSKeys"]
                ["Secondary"])

        else:
            # Read all of our LOCAL Secrets for Accessing IoT Central
            self.logger.info("[USING LOCAL SECRETS]")
            scope_id = secret_client.get_secret(
                secrets.data["LocalSecrets"]["ScopeId"])
            device_primary_key = secret_client.get_secret(
                secrets.data["LocalSecrets"]["DeviceConnect"]["SaSKeys"]
                ["Primary"])
            device_secondary_key = secret_client.get_secret(
                secrets.data["LocalSecrets"]["DeviceConnect"]["SaSKeys"]
                ["Secondary"])
            gateway_primary_key = secret_client.get_secret(
                secrets.data["LocalSecrets"]["GatewayConnect"]["SaSKeys"]
                ["Primary"])
            gateway_secondary_key = secret_client.get_secret(
                secrets.data["LocalSecrets"]["GatewayConnect"]["SaSKeys"]
                ["Secondary"])

            # Verbose
            self.logger.info("[SCOPE ID]: %s" % scope_id.value)
            self.logger.info("[DEVICE PRIMARY KEY]: %s" %
                             device_primary_key.value)
            self.logger.info("[DEVICE SECONDARY KEY]: %s" %
                             device_secondary_key.value)
            self.logger.info("[GATEWAY PRIMARY KEY]: %s" %
                             gateway_primary_key.value)
            self.logger.info("[GATEWAY SECONDARY KEY]: %s" %
                             gateway_secondary_key.value)

        # Symetric Key for handling Device Specific SaS Keys
        symmetrickey = SymmetricKey(self.logger)

        try:
            # Iterate the Discovered Devices and Provision
            # the devicescache.json file element [DeviceNamePrefix]...
            for device in self.devices_provision["Devices"]:
                provision_this_device = False

                if (self.provisioning_scope == "ALL"):
                    provision_this_device = True
                elif (self.provisioning_scope == "NEW"
                      and device["LastProvisioned"] == None):
                    provision_this_device = True
                elif (self.provisioning_scope == device["DeviceName"]):
                    provision_this_device = True

                if provision_this_device:
                    # Get a Device Specific Symetric Key
                    device_symmetrickey = symmetrickey.compute_derived_symmetric_key(
                        device["DeviceName"], device_secondary_key.value)
                    self.logger.info("[SYMETRIC KEY] %s" % device_symmetrickey)

                    # Provision the Device
                    self.logger.warning("[PROVISIONING] %s" %
                                        device["DeviceName"])
                    provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
                        provisioning_host=secrets.data["ProvisioningHost"],
                        registration_id=device["DeviceName"],
                        id_scope=scope_id.value,
                        symmetric_key=device_symmetrickey,
                        websockets=True)

                    provisioning_device_client.provisioning_payload = '{"iotcModelId":"%s"}' % (
                        device["DCM"])
                    registration_result = await provisioning_device_client.register(
                    )

                    newDevice = {
                        "DeviceName":
                        device["DeviceName"],
                        "Address":
                        device["Address"],
                        "LastRSSI":
                        device["LastRSSI"],
                        "DCM":
                        device["DCM"],
                        "DeviceInfoInterface":
                        device["DeviceInfoInterface"],
                        "DeviceInfoInterfaceInstanceName":
                        device["DeviceInfoInterfaceInstanceName"],
                        "NanoBLEInterface":
                        device["NanoBLEInterface"],
                        "NanoBLEInterfaceInstanceName":
                        device["NanoBLEInterfaceInstanceName"],
                        "LastProvisioned":
                        str(datetime.datetime.now())
                    }
                    self.data["Devices"].append(newDevice)
                    continue
                else:
                    newDevice = {
                        "DeviceName":
                        device["DeviceName"],
                        "Address":
                        device["Address"],
                        "LastRSSI":
                        device["LastRSSI"],
                        "DCM":
                        device["DCM"],
                        "DeviceInfoInterface":
                        device["DeviceInfoInterface"],
                        "DeviceInfoInterfaceInstanceName":
                        device["DeviceInfoInterfaceInstanceName"],
                        "NanoBLEInterface":
                        device["NanoBLEInterface"],
                        "NanoBLEInterfaceInstanceName":
                        device["NanoBLEInterfaceInstanceName"],
                        "LastProvisioned":
                        None
                    }
                    self.data["Devices"].append(newDevice)
                    continue

        except Exception as ex:
            self.logger.error("[ERROR] %s" % ex)
            self.logger.error(
                "[TERMINATING] We encountered an error provisioning for BLE Devices"
            )
            return

        # Update the Cache
        devicescache.update_file(self.data)
        return