I would like to send the telemetry data from the IOT hub to Azure digital twins using Python instead of C#( I don't have experience with C#), but I can't get it.
I have followed the complete example from Microsoft (https://learn.microsoft.com/en-us/azure/digital-twins/how-to-ingest-iot-hub-data) without success.
This is my code for Simulated Device. I checked and it successfully send the message to IoT Hub:
from azure.iot.device import IoTHubDeviceClient
import json
import random
import time
CONNECTION_STRING = "<DeviceIDConnectionString>"
def simulate_telemetry(device_client):
while True:
temperature = random.randint(20, 30)
humidity = random.randint(60, 80)
telemetry = {
"temperature": temperature,
"humidity": humidity
}
message = json.dumps(telemetry)
device_client.send_message(message)
print(f"Sent message: {message}")
time.sleep(1)
if __name__ == "__main__":
device_client = IoTHubDeviceClient.create_from_connection_string(CONNECTION_STRING)
device_client.connect()
simulate_telemetry(device_client)
And this is the code for function app using python and event grid trigger ( I followed the C# code in the tutorial link):
import os
import logging
import json
from azure.identity import DefaultAzureCredential
from azure.digitaltwins.core import DigitalTwinsClient
from azure.core.exceptions import ResourceNotFoundError
import azure.functions as func
adt_instance_url = os.getenv("DIGITAL_TWINS_URL")
if adt_instance_url is None:
logging.error("Application setting 'DIGITAL_TWINS_URL' not set")
async def main(event: func.EventGridEvent):
try:
# Authenticate with Digital Twins
credential = DefaultAzureCredential()
client = DigitalTwinsClient(adt_instance_url, credential)
logging.info("ADT service client connection created.")
if event is not None and event.data is not None:
logging.info(event.data)
# Find device ID and temperature
device_message = event.get_json()
device_id = device_message["systemProperties"]["iothub-connection-device-id"]
temperature = device_message["body"]["temperature"]
humidity = device_message["body"]["humidity"]
logging.info(f"Device: {device_id} Temperature is: {temperature} Humidity is : {humidity}")
# Update twin with device temperature
twin_id = device_id
patch = [
{
"op": "replace",
"path": "/Temperature",
"value": temperature
},
{
"op": "replace",
"path": "/Humidity",
"value": humidity
}
]
await client.update_digital_twin(twin_id, patch)
except ResourceNotFoundError:
logging.error(f"Digital twin with ID {twin_id} not found.")
except Exception as ex:
logging.error(f"Error in ingest function: {ex}")
I also create the event subscription to connect IoT hub and Function App already. I follow all the step in tutorial.
When I run the function app locally on VS code ( Ubuntu 20.04), it executed successfully but have this error in between:
Error in ingest function: 'NoneType' object has no attribute 'startswith'
I only use these 2 files code for whole project.
I run the query on Azure Digital Twins explorer but I don't see the twin updated as expected.
Should I add something else (code) or what can I change? I think maybe the problem is missing the Digital Twins as output or IoT Hub as input.
I have tested the telemetry ingestion with Python Code and here is a sample that I have tested which works.
1 Sample for IoT Hub data simulation
2 Azure Event Grid trigger function
I have published the code to Azure function App and assigned "Azure Digital Twins Data Owner" role access to the function app. I have also configured the function app to receive telemetry event data from the IoT Hub. Please refer the sections Assign an access role and Connect the function to IoT Hub for steps needed to do this.
Once I have everything configured, I could see the events received to the cloud Azure function and could update the Azure digital twin. Please refer the below image for reference.
Please also ensure that your Azure Digital Twin has an initial value loaded to the temperature property as we are using
replacemethod patch to update the Twin.