async def connect(self): try: # load the secrets secrets = Secrets(self.logger) secrets.init() self.device_secrets = secrets.get_device_secrets(self.device_name) print("here secrets") print(self.device_secrets) self.device_client = IoTHubDeviceClient.create_from_symmetric_key( symmetric_key=self.device_secrets["Device"]["Secrets"] ["DeviceSymmetricKey"], hostname=self.device_secrets["Device"]["Secrets"] ["AssignedHub"], device_id=self.device_name, websockets=True) await self.device_client.connect() self.logger.info("[DEVICE CLIENT] %s" % self.device_client) except Exception as ex: self.logger.error("[ERROR] %s" % ex) self.logger.error( "[TERMINATING] We encountered an error creating and connecting the device in the Class::DeviceClient" ) return None return
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 = []
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
def __init__(self, Log): self.logger = Log # Load configuration config = Config(self.logger) self.config = config.data # Secrets Cache self.secrets = Secrets(self.logger) self.secrets_data = self.secrets.data self.serial_emulator_port = None # Configure the National Renewable Energy Labratory self.NREUrl = self.config["NREUrl"].format(api_key = self.secrets_data["NREApiKey"])
def __init__(self, Log, DeviceName): self.logger = Log # Load configuration config = Config(self.logger) self.config = config.data # Load the secrets secrets = Secrets(self.logger) self.secrets = secrets.data # Azure Device self.device_name = DeviceName self.device_secrets = [] self.device_client = None # Logging Mappers data = [ x for x in self.config["ClassLoggingMaps"] if x["Name"] == "DeviceClient" ] self.class_name_map = data[0]["LoggingId"]
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