Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions samples/azure-iot-direct-method-sample-python-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
config/
.env
/tests/__pycache__/*.pyc
13 changes: 13 additions & 0 deletions samples/azure-iot-direct-method-sample-python-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Azure IoT Edge sample for configuration

This is a sample of using Azure IoT Direct Method to update configuration parameter in the IoT Edge module.

Temperature Simulator IoT Edge module sends simulated telemetry to the ConfigModule IoT Edge module at default frequency.

ConfigModule Module Direct Method can be invoked from IoT Hub. Once a new frequecy value is passed to edge via Module Direct Method, ConfigModule Direct Method handler will receive the new frequency value and update Temperature Simulator's telemetry sending frequency accordingly.

[Azure IoT Python SDK v2](https://github.com/Azure/azure-iot-sdk-python) is in use.




Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"modulesContent": {
"$edgeAgent": {
"properties.desired": {
"modules": {
"tempSensor": {
"settings": {
"image": "mcr.microsoft.com/azureiotedge-simulated-temperature-sensor:1.0",
"createOptions": "{}"
},
"type": "docker",
"version": "1.0",
"status": "running",
"restartPolicy": "always"
},

"ConfigModule": {
"settings": {
"image": "<module-image>",
"createOptions": "{}"
},
"type": "docker",
"version": "1.0",
"status": "running",
"restartPolicy": "always"
}
},
"runtime": {
"settings": {
"minDockerVersion": "v1.25",
"registryCredentials": {
"my_acr": {
"address": "",
"password": "",
"username": ""
}
}
},
"type": "docker"
},
"schemaVersion": "1.1",
"systemModules": {
"edgeAgent": {
"settings": {
"image": "mcr.microsoft.com/azureiotedge-agent:1.2",
"createOptions": "{}"
},
"type": "docker"
},
"edgeHub": {
"settings": {
"image": "mcr.microsoft.com/azureiotedge-hub:1.2",
"createOptions": "{\"HostConfig\":{\"PortBindings\":{\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}],\"443/tcp\":[{\"HostPort\":\"443\"}]}}}"
},
"type": "docker",
"status": "running",
"restartPolicy": "always"
}
}
}
},
"$edgeHub": {
"properties.desired": {
"routes": {
"ConfigModuleToIoTHub": "FROM /messages/modules/ConfigModule/outputs/* INTO $upstream",
"sensorToConfigModule": "FROM /messages/modules/tempSensor/outputs/temperatureOutput INTO BrokeredEndpoint(\"/modules/ConfigModule/inputs/input1\")"
},
"schemaVersion": "1.1",
"storeAndForwardConfiguration": {
"timeToLiveSecs": 7200
}
}
},
"tempSensor": {
"properties.desired": {
"SendData": true,
"SendInterval": 1,
"StopAfterCount": 2000
}
},
"ConfigModule": {
"properties.desired": {}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pycodestyle]
max_line_length = 150
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# syntax=docker/dockerfile:1
FROM python:3.7-slim-buster

WORKDIR /app

RUN apt-get update && \
apt-get install -y --no-install-recommends libcurl4-openssl-dev libboost-python-dev libpython3-dev && \
rm -rf /var/lib/apt/lists/*
RUN pip3 install --upgrade pip
RUN pip3 install setuptools
RUN pip3 install ptvsd==4.1.3
COPY requirements.txt ./
RUN pip3 install -r requirements.txt

COPY . .

RUN useradd -ms /bin/bash moduleuser
USER moduleuser

CMD [ "python3", "-u", "./main.py" ]

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# syntax=docker/dockerfile:1
FROM python:3.7-slim-buster

WORKDIR /app

RUN apt-get update && \
apt-get install -y --no-install-recommends libcurl4-openssl-dev libboost-python-dev libpython3-dev && \
rm -rf /var/lib/apt/lists/*
RUN pip3 install --upgrade pip
RUN pip3 install setuptools
RUN pip3 install ptvsd==4.1.3
COPY requirements.txt ./
RUN pip3 install -r requirements.txt

COPY . .

RUN useradd -ms /bin/bash moduleuser
USER moduleuser

CMD [ "python3", "-u", "./main.py" ]

Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for
# full license information.
# Migrated with IoTHub Python SDK v2

import asyncio
import time
import sys
import json
from threading import Lock
import logging
# from azure.core.credentials import AzureKeyCredential
from azure.iot.device.aio import IoTHubModuleClient
from azure.iot.device import Message, MethodResponse
from datetime import datetime

logging.basicConfig(level=logging.DEBUG)

mutex = Lock()
# global counters
TIME_INTERVAL = 10 # in sec
CONFIG_TELE = "configmsg"


class HubManager(object):
def __init__(self):
self.module_client = IoTHubModuleClient.create_from_edge_environment()

async def start(self):
await self.module_client.connect()
# set the received data handlers on the client
self.module_client.on_message_received = self.message_handler
self.module_client.on_method_request_received = self.method_handler

async def preprocess(self, message):
# print("in preprocess function!")
message_str = message.data
if not message_str:
return None
message_obj = json.loads(message_str)
print("module receives a msg with temp: {}".format(message_obj["machine"]["temperature"]))
input_data = message_obj["machine"]["temperature"]
time_stamp = message_obj["timeCreated"] # UTC iso format. "1972-01-01T00:00:00Z" is UTC ISO 8601

return [time_stamp, input_data]

async def config_telemetry(self, message, **kwargs):
# print("in config_telemetry")

for key, value in kwargs.items():
if key == "time_interval":# default time_interval value = 10
print("config_telemetry time_interval ", value)
print("message ts ", message[0], type(message[0]))

for iter_sec in range(0, 60, value):
print("iter_sec", iter_sec)
print('message[0][17:19].strip()', message[0][17:19].strip(), type(message[0][17:19].strip()))
if int(message[0][17:19].strip()) == iter_sec:
message_str = json.dumps(message)
result = Message(message_str)
print("telemetry to be send to iothub: ", message_str)
await self.forward_event_to_output(result, "output2")


async def message_handler(self, message):
print("in message_handler")
print ("message_handler TIME_INTERVAL", TIME_INTERVAL)
if message.input_name == "input1":
mutex.acquire()
try:
sensor_input = await self.preprocess(message)
if sensor_input!= None:
print("config freq and sending...")
await self.config_telemetry(sensor_input, time_interval= TIME_INTERVAL)
except Exception as e:
print("Error when config telemetry: %s" % e)
finally:
mutex.release()

else:
print("message received on unknown input")


# Direct Method receiver
async def method_handler(self, method_request):
print("Received method [%s]" % (method_request.name))
print("config_tele_messsage: ",method_request.payload)
global TIME_INTERVAL
TIME_INTERVAL = method_request.payload
print ("method_handler TIME_INTERVAL", TIME_INTERVAL)
print("Sent method response to module output via event [%s]" % CONFIG_TELE)
method_response = MethodResponse.create_from_method_request(
method_request, 200, "{ \"Response\": \"This is the response from the device. \" }"
)
await self.module_client.send_method_response(method_response)

async def forward_event_to_output(self, event, moduleOutputName):
await self.module_client.send_message_to_output(event, moduleOutputName)

async def main():
try:
print("\nPython %s\n" % sys.version)
print("Prototype for config IoT Edge module")

hub_manager = HubManager()
await hub_manager.start()
print("The sample is now waiting for messages and will indefinitely. Press Ctrl-C to exit. ")

while True:
time.sleep(1)

except KeyboardInterrupt:
await hub_manager.module_client.shutdown()
print("Configuration sample stopped")


if __name__ == '__main__':
asyncio.run(main())
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema-version": "0.0.1",
"description": "",
"image": {
"repository": "${ACR_ADDRESS}/configmodule",
"tag": {
"version": "0.0.${BUILD_BUILDID}",
"platforms": {
"amd64": "./Dockerfile.amd64",
"amd64.debug": "./Dockerfile.amd64.debug",
"arm32v7": "./Dockerfile.arm32v7"
}
},
"buildOptions": []
},
"language": "python"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pathlib==1.0.1
ruamel-yaml==0.17.16
azure-iot-device==2.7.1