diff --git a/cookiecutter.json b/cookiecutter.json new file mode 100644 index 0000000..bcd09ba --- /dev/null +++ b/cookiecutter.json @@ -0,0 +1,4 @@ +{ + "module_name": "SampleModule", + "image_repository": "localhost:5000/samplemodule" +} \ No newline at end of file diff --git a/{{cookiecutter.module_name}}/.gitignore b/{{cookiecutter.module_name}}/.gitignore new file mode 100644 index 0000000..894a44c --- /dev/null +++ b/{{cookiecutter.module_name}}/.gitignore @@ -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/ diff --git a/{{cookiecutter.module_name}}/Dockerfile b/{{cookiecutter.module_name}}/Dockerfile new file mode 100644 index 0000000..f29a93c --- /dev/null +++ b/{{cookiecutter.module_name}}/Dockerfile @@ -0,0 +1,16 @@ +FROM ubuntu:xenial + +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + python \ + python-pip \ + libboost1.58-all-dev \ + libcurl4-openssl-dev + +COPY requirements.txt ./ +RUN pip install -r requirements.txt + +COPY . . + +CMD [ "python", "-u", "./main.py" ] \ No newline at end of file diff --git a/{{cookiecutter.module_name}}/main.py b/{{cookiecutter.module_name}}/main.py new file mode 100644 index 0000000..ec22501 --- /dev/null +++ b/{{cookiecutter.module_name}}/main.py @@ -0,0 +1,122 @@ +# Copyright (c) Microsoft. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for +# full license information. + +import os +import random +import time +import sys +import iothub_client +from iothub_client import IoTHubClient, IoTHubClientError, IoTHubTransportProvider +from iothub_client import IoTHubMessage, IoTHubMessageDispositionResult, IoTHubError + +# messageTimeout - the maximum time in milliseconds until a message times out. +# The timeout period starts at IoTHubClient.send_event_async. +# By default, messages do not expire. +MESSAGE_TIMEOUT = 10000 + +# global counters +RECEIVE_CALLBACKS = 0 +SEND_CALLBACKS = 0 + +# Choose HTTP, AMQP or MQTT as transport protocol. Currently only MQTT is supported. +PROTOCOL = IoTHubTransportProvider.MQTT + +# String containing Hostname, Device Id & Device Key & Module Id in the format: +# "HostName=;DeviceId=;SharedAccessKey=;ModuleId=;GatewayHostName=" +CONNECTION_STRING = "[Device Connection String]" + +# Callback received when the message that we're forwarding is processed. +def send_confirmation_callback(message, result, user_context): + global SEND_CALLBACKS + print ( "Confirmation[%d] received for message with result = %s" % (user_context, result) ) + map_properties = message.properties() + key_value_pair = map_properties.get_internals() + print ( " Properties: %s" % key_value_pair ) + SEND_CALLBACKS += 1 + print ( " Total calls confirmed: %d" % SEND_CALLBACKS ) + + +# receive_message_callback is invoked when an incoming message arrives on the specified +# input queue (in the case of this sample, "input1"). Because this is a filter module, +# we will forward this message onto the "output1" queue. +def receive_message_callback(message, hubManager): + global RECEIVE_CALLBACKS + message_buffer = message.get_bytearray() + size = len(message_buffer) + print ( " Data: <<<%s>>> & Size=%d" % (message_buffer[:size].decode('utf-8'), size) ) + map_properties = message.properties() + key_value_pair = map_properties.get_internals() + print ( " Properties: %s" % key_value_pair ) + RECEIVE_CALLBACKS += 1 + print ( " Total calls received: %d" % RECEIVE_CALLBACKS ) + hubManager.forward_event_to_output("output1", message, 0) + return IoTHubMessageDispositionResult.ACCEPTED + + +class HubManager(object): + + def __init__( + self, + connection_string): + self.client_protocol = PROTOCOL + self.client = IoTHubClient(connection_string, PROTOCOL) + + # set the time until a message times out + self.client.set_option("messageTimeout", MESSAGE_TIMEOUT) + # some embedded platforms need certificate information + self.set_certificates() + + # sets the callback when a message arrives on "input1" queue. Messages sent to + # other inputs or to the default will be silently discarded. + self.client.set_message_callback("input1", receive_message_callback, self) + + def set_certificates(self): + isWindows = sys.platform.lower() in ['windows', 'win32'] + if not isWindows: + CERT_FILE = os.environ['EdgeModuleCACertificateFile'] + print("Adding TrustedCerts from: {0}".format(CERT_FILE)) + + # this brings in x509 privateKey and certificate + file = open(CERT_FILE) + try: + self.client.set_option("TrustedCerts", file.read()) + print ( "set_option TrustedCerts successful" ) + except IoTHubClientError as iothub_client_error: + print ( "set_option TrustedCerts failed (%s)" % iothub_client_error ) + + file.close() + + # Forwards the message received onto the next stage in the process. + def forward_event_to_output(self, outputQueueName, event, send_context): + self.client.send_event_async( + outputQueueName, event, send_confirmation_callback, send_context) + +def main(connection_string): + try: + print ( "\nPython %s\n" % sys.version ) + print ( "IoT Hub Client for Python" ) + + hub_manager = HubManager(connection_string) + + print ( "Starting the IoT Hub Python sample using protocol %s..." % hub_manager.client_protocol ) + print ( "The sample is now waiting for messages and will indefinitely. Press Ctrl-C to exit. ") + + while True: + time.sleep(1000) + + except IoTHubError as iothub_error: + print ( "Unexpected error %s from IoTHub" % iothub_error ) + return + except KeyboardInterrupt: + print ( "IoTHubClient sample stopped" ) + +if __name__ == '__main__': + try: + CONNECTION_STRING = os.environ['EdgeHubConnectionString'] + + except Exception as error: + print ( error ) + sys.exit(1) + + main(CONNECTION_STRING) \ No newline at end of file diff --git a/{{cookiecutter.module_name}}/module.json b/{{cookiecutter.module_name}}/module.json new file mode 100644 index 0000000..1be9af6 --- /dev/null +++ b/{{cookiecutter.module_name}}/module.json @@ -0,0 +1,14 @@ +{ + "$schema-version": "0.0.1", + "description": "", + "image": { + "repository": "{{cookiecutter.image_repository}}", + "tag": { + "version": "0.0.1", + "platforms": { + "amd64": "./Dockerfile" + } + } + }, + "language": "python" +} \ No newline at end of file diff --git a/{{cookiecutter.module_name}}/requirements.txt b/{{cookiecutter.module_name}}/requirements.txt new file mode 100644 index 0000000..79b971b --- /dev/null +++ b/{{cookiecutter.module_name}}/requirements.txt @@ -0,0 +1 @@ +azure-iothub-device-client==1.3.0.0b0 \ No newline at end of file