Safe device declaration from xlsx (example file from Bell project provided)
This commit is contained in:
157
Python/create_iot_hub_devices_from_excel.py
Normal file
157
Python/create_iot_hub_devices_from_excel.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import os
|
||||
import pandas as pd
|
||||
from dotenv import load_dotenv
|
||||
from azure.iot.hub import IoTHubRegistryManager
|
||||
from azure.iot.hub.models import Twin, Device
|
||||
from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError
|
||||
from msrest.exceptions import HttpOperationError
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# --- Configuration ---
|
||||
IOTHUB_CONNECTION_STRING = os.getenv("CONNECTION_STRING_INOX_PROD")
|
||||
INPUT_EXCEL_FILE = 'BELL_DEVICE_LIST.xlsx'
|
||||
OUTPUT_EXCEL_FILE = 'BELL_DEVICE_LIST_CS.xlsx'
|
||||
|
||||
def get_connection_string(device: Device, host_name: str) -> str:
|
||||
"""
|
||||
Constructs the primary connection string for a given device object.
|
||||
|
||||
:param device: The IoTHubRegistryManager Device object.
|
||||
:param host_name: The IoT Hub hostname (e.g., 'your-hub.azure-devices.net').
|
||||
:return: The device connection string.
|
||||
"""
|
||||
if not device or not device.authentication or not device.authentication.symmetric_key:
|
||||
return "Error: Could not retrieve symmetric key."
|
||||
|
||||
primary_key = device.authentication.symmetric_key.primary_key
|
||||
connection_string = (
|
||||
f"HostName={host_name};"
|
||||
f"DeviceId={device.device_id};"
|
||||
f"SharedAccessKey={primary_key}"
|
||||
)
|
||||
return connection_string
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function to provision devices from an Excel file and save results.
|
||||
"""
|
||||
if not IOTHUB_CONNECTION_STRING:
|
||||
print("Error: The IOTHUB_CONNECTION_STRING environment variable is not set.")
|
||||
return
|
||||
|
||||
try:
|
||||
registry_manager = IoTHubRegistryManager.from_connection_string(IOTHUB_CONNECTION_STRING)
|
||||
# Extract the hub hostname once for use in connection strings
|
||||
host_name = IOTHUB_CONNECTION_STRING.split(';')[0].split('=')[1]
|
||||
|
||||
df = pd.read_excel(INPUT_EXCEL_FILE)
|
||||
# Add the new column to store connection strings, initialized as empty
|
||||
df['connectionString'] = ''
|
||||
|
||||
for index, row in df.iterrows():
|
||||
device_id = row['deviceId']
|
||||
device_type = row['deviceType']
|
||||
site = row['site']
|
||||
number = row['number']
|
||||
version = row.get('version')
|
||||
|
||||
print(f"\n--- Processing Device: {device_id} ---")
|
||||
|
||||
final_device_obj = None # Will hold the device object after creation/validation
|
||||
|
||||
try:
|
||||
# 1. CHECK IF DEVICE EXISTS
|
||||
existing_device = registry_manager.get_device(device_id)
|
||||
print(f"Device '{device_id}' already exists. Validating configuration...")
|
||||
|
||||
# 2. VALIDATE IOT EDGE STATUS
|
||||
is_edge_in_hub = existing_device.capabilities.iot_edge
|
||||
is_edge_in_file = (device_type == 'AC_GATEWAY')
|
||||
|
||||
if is_edge_in_hub != is_edge_in_file:
|
||||
print(f" [MISMATCH] IoT Edge status is incorrect. Deleting and recreating.")
|
||||
registry_manager.delete_device(device_id)
|
||||
final_device_obj = create_device_from_spec(registry_manager, device_id, device_type, site, number, version)
|
||||
else:
|
||||
print(" [OK] IoT Edge status is correct. Updating tags...")
|
||||
update_device_tags(registry_manager, device_id, device_type, site, number, version)
|
||||
final_device_obj = existing_device
|
||||
|
||||
except HttpOperationError as http_err:
|
||||
# Check the status code on the nested 'response' object
|
||||
if http_err.response.status_code == 404:
|
||||
print(f"Device '{device_id}' does not exist. Creating...")
|
||||
final_device_obj = create_device_from_spec(registry_manager, device_id, device_type, site, number, version)
|
||||
else:
|
||||
# It's a different HTTP error (e.g., 401 Unauthorized, 503 Service Unavailable)
|
||||
print(f" [ERROR] An unexpected HTTP error occurred for {device_id}:")
|
||||
# The response body often has the most detailed error message
|
||||
if http_err.response:
|
||||
print(f" Status Code: {http_err.response.status_code}")
|
||||
print(f" Response Text: {http_err.response.text}")
|
||||
else:
|
||||
print(f" Full Error: {http_err}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{e.__class__} {e.__str__}")
|
||||
|
||||
# 4. IF A DEVICE WAS CREATED OR VALIDATED, GET AND SAVE ITS CONNECTION STRING
|
||||
if final_device_obj:
|
||||
connection_string = get_connection_string(final_device_obj, host_name)
|
||||
# Use .at for efficient, label-based assignment
|
||||
df.at[index, 'connectionString'] = connection_string
|
||||
print(f" > Connection string recorded for '{device_id}'.")
|
||||
|
||||
# 5. SAVE THE UPDATED DATAFRAME TO A NEW EXCEL FILE
|
||||
df.to_excel(OUTPUT_EXCEL_FILE, index=False)
|
||||
print(f"\n--- Processing Complete ---")
|
||||
print(f"Results, including connection strings, have been saved to '{OUTPUT_EXCEL_FILE}'.")
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"Error: The input file '{INPUT_EXCEL_FILE}' was not found.")
|
||||
except Exception as e:
|
||||
print(f"A general error occurred: {e}")
|
||||
|
||||
def create_device_from_spec(registry_manager, device_id, device_type, site, number, version) -> Device:
|
||||
"""Creates a device, sets its tags, and returns the created device object."""
|
||||
try:
|
||||
is_edge = (device_type == 'AC_GATEWAY')
|
||||
|
||||
device = registry_manager.create_device_with_sas(
|
||||
device_id, "", "", status="enabled", iot_edge=is_edge
|
||||
)
|
||||
print(f" > Created {'Edge' if is_edge else 'non-Edge'} device '{device_id}'.")
|
||||
|
||||
# After creating, update the tags
|
||||
update_device_tags(registry_manager, device_id, device_type, site, number, version)
|
||||
return device # Return the newly created device object
|
||||
|
||||
except ResourceExistsError:
|
||||
print(f" [WARNING] Device {device_id} already exists. Attempting to retrieve it.")
|
||||
return registry_manager.get_device(device_id)
|
||||
except Exception as e:
|
||||
print(f" [ERROR] Failed to create device {device_id}: {e}")
|
||||
return None
|
||||
|
||||
def update_device_tags(registry_manager, device_id, device_type, site, number, version):
|
||||
"""Updates the device twin tags based on its type."""
|
||||
try:
|
||||
tags = {
|
||||
"site": site,
|
||||
"number": int(number),
|
||||
"deviceType": device_type
|
||||
}
|
||||
if device_type == 'AC_GATEWAY':
|
||||
tags["version"] = str(version)
|
||||
|
||||
twin = registry_manager.get_twin(device_id)
|
||||
twin_patch = Twin(tags=tags)
|
||||
registry_manager.update_twin(device_id, twin_patch, twin.etag)
|
||||
print(f" > Successfully updated tags for '{device_id}'.")
|
||||
|
||||
except Exception as e:
|
||||
print(f" [ERROR] Failed to update tags for {device_id}: {e}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user