From 75af72b6b80388f2365c736039deb8507b11451d Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Fri, 21 Jan 2022 23:57:11 -0800 Subject: [PATCH 01/11] Fix miniMQTT compatibility and native_networking examples --- .gitignore | 201 +++++++++++++++++- README.rst | 2 +- adafruit_azureiot/device_registration.py | 11 +- adafruit_azureiot/iot_mqtt.py | 11 +- .../azureiot_central_commands.py | 2 +- .../azureiot_central_notconnected.py | 2 +- .../azureiot_central_properties.py | 2 +- .../azureiot_central_simpletest.py | 2 +- .../azureiot_hub_directmethods.py | 0 .../{ => esp32spi }/azureiot_hub_messages.py | 0 .../azureiot_hub_simpletest.py | 0 .../azureiot_hub_twin_operations.py | 0 examples/azureiot_secrets_example.py | 2 +- .../azureiot_central_simpletest.py | 111 ++++++++++ .../azureiot_hub_simpletest.py | 106 +++++++++ 15 files changed, 430 insertions(+), 22 deletions(-) rename examples/{ => esp32spi }/azureiot_central_commands.py (99%) rename examples/{ => esp32spi }/azureiot_central_notconnected.py (99%) rename examples/{ => esp32spi }/azureiot_central_properties.py (99%) rename examples/{ => esp32spi }/azureiot_central_simpletest.py (99%) rename examples/{ => esp32spi }/azureiot_hub_directmethods.py (100%) rename examples/{ => esp32spi }/azureiot_hub_messages.py (100%) rename examples/{ => esp32spi }/azureiot_hub_simpletest.py (100%) rename examples/{ => esp32spi }/azureiot_hub_twin_operations.py (100%) create mode 100644 examples/native_networking/azureiot_central_simpletest.py create mode 100644 examples/native_networking/azureiot_hub_simpletest.py diff --git a/.gitignore b/.gitignore index 28342c8..fadf338 100644 --- a/.gitignore +++ b/.gitignore @@ -3,15 +3,200 @@ # SPDX-License-Identifier: Unlicense *.mpy -.idea -__pycache__ _build *.pyc -.env bundles -*.DS_Store -.eggs -dist -**/*.egg-info -.vscode/settings.json + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +# 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/ +share/python-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/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# 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/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/README.rst b/README.rst index 419028f..8fcaf35 100644 --- a/README.rst +++ b/README.rst @@ -203,7 +203,7 @@ To use Azure IoT Central, you will need to create an Azure IoT Central app, crea from adafruit_azureiot import IoTCentralDevice - device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"]) + device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["sas_key"]) device.connect() Once the device is connected, you will regularly need to run a ``loop`` to poll for messages from the cloud. diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index e5fa2c5..13dd0ed 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -14,10 +14,13 @@ """ import json +import ssl import time + import adafruit_logging as logging from adafruit_logging import Logger -import adafruit_minimqtt.adafruit_minimqtt as minimqtt +import adafruit_minimqtt.adafruit_minimqtt as MQTT + from . import constants from .quote import quote from .keys import compute_derived_symmetric_key @@ -177,16 +180,16 @@ def register_device(self, expiry: int) -> str: sig_encoded = quote(sig_no_encode, "~()*!.'") auth_string = f"SharedAccessSignature sr={sr}&sig={sig_encoded}&se={str(expiry)}&skn=registration" - minimqtt.set_socket(self._socket, self._iface) + MQTT.set_socket(self._socket, self._iface) - self._mqtt = minimqtt.MQTT( + self._mqtt = MQTT.MQTT( broker=constants.DPS_END_POINT, username=username, password=auth_string, port=8883, keep_alive=120, - is_ssl=True, client_id=self._device_id, + ssl_context=ssl.create_default_context(), ) self._mqtt.enable_logger(logging, self._logger.getEffectiveLevel()) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index cdc7228..d2a7d99 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -14,9 +14,12 @@ import gc import json +import ssl import time -import adafruit_minimqtt.adafruit_minimqtt as minimqtt + +import adafruit_minimqtt.adafruit_minimqtt as MQTT import adafruit_logging as logging + from .iot_error import IoTError from .keys import compute_derived_symmetric_key from .quote import quote @@ -108,7 +111,7 @@ def _gen_sas_token(self) -> str: return token def _create_mqtt_client(self) -> None: - minimqtt.set_socket(self._socket, self._iface) + MQTT.set_socket(self._socket, self._iface) self._logger.debug( str.replace( @@ -118,14 +121,14 @@ def _create_mqtt_client(self) -> None: ) ) - self._mqtts = minimqtt.MQTT( + self._mqtts = MQTT.MQTT( broker=self._hostname, username=self._username, password=self._passwd, port=8883, keep_alive=120, - is_ssl=True, client_id=self._device_id, + ssl_context=ssl.create_default_context(), ) self._mqtts.enable_logger(logging, self._logger.getEffectiveLevel()) diff --git a/examples/azureiot_central_commands.py b/examples/ esp32spi /azureiot_central_commands.py similarity index 99% rename from examples/azureiot_central_commands.py rename to examples/ esp32spi /azureiot_central_commands.py index 8ae0e15..602c0d8 100644 --- a/examples/azureiot_central_commands.py +++ b/examples/ esp32spi /azureiot_central_commands.py @@ -99,7 +99,7 @@ # Create an IoT Hub device client and connect device = IoTCentralDevice( - socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"] + socket, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] ) # Subscribe to commands diff --git a/examples/azureiot_central_notconnected.py b/examples/ esp32spi /azureiot_central_notconnected.py similarity index 99% rename from examples/azureiot_central_notconnected.py rename to examples/ esp32spi /azureiot_central_notconnected.py index eda9bdb..3853611 100644 --- a/examples/azureiot_central_notconnected.py +++ b/examples/ esp32spi /azureiot_central_notconnected.py @@ -103,7 +103,7 @@ # Create an IoT Hub device client and connect device = IoTCentralDevice( - socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"] + socket, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] ) # don't connect diff --git a/examples/azureiot_central_properties.py b/examples/ esp32spi /azureiot_central_properties.py similarity index 99% rename from examples/azureiot_central_properties.py rename to examples/ esp32spi /azureiot_central_properties.py index c5bebca..252f228 100644 --- a/examples/azureiot_central_properties.py +++ b/examples/ esp32spi /azureiot_central_properties.py @@ -96,7 +96,7 @@ # Create an IoT Hub device client and connect device = IoTCentralDevice( - socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"] + socket, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] ) # Subscribe to property changes diff --git a/examples/azureiot_central_simpletest.py b/examples/ esp32spi /azureiot_central_simpletest.py similarity index 99% rename from examples/azureiot_central_simpletest.py rename to examples/ esp32spi /azureiot_central_simpletest.py index 49a81af..7999cd3 100644 --- a/examples/azureiot_central_simpletest.py +++ b/examples/ esp32spi /azureiot_central_simpletest.py @@ -97,7 +97,7 @@ # Create an IoT Hub device client and connect device = IoTCentralDevice( - socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"] + socket, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] ) print("Connecting to Azure IoT Central...") diff --git a/examples/azureiot_hub_directmethods.py b/examples/ esp32spi /azureiot_hub_directmethods.py similarity index 100% rename from examples/azureiot_hub_directmethods.py rename to examples/ esp32spi /azureiot_hub_directmethods.py diff --git a/examples/azureiot_hub_messages.py b/examples/ esp32spi /azureiot_hub_messages.py similarity index 100% rename from examples/azureiot_hub_messages.py rename to examples/ esp32spi /azureiot_hub_messages.py diff --git a/examples/azureiot_hub_simpletest.py b/examples/ esp32spi /azureiot_hub_simpletest.py similarity index 100% rename from examples/azureiot_hub_simpletest.py rename to examples/ esp32spi /azureiot_hub_simpletest.py diff --git a/examples/azureiot_hub_twin_operations.py b/examples/ esp32spi /azureiot_hub_twin_operations.py similarity index 100% rename from examples/azureiot_hub_twin_operations.py rename to examples/ esp32spi /azureiot_hub_twin_operations.py diff --git a/examples/azureiot_secrets_example.py b/examples/azureiot_secrets_example.py index 7eeefde..4f2a2e0 100644 --- a/examples/azureiot_secrets_example.py +++ b/examples/azureiot_secrets_example.py @@ -27,7 +27,7 @@ # key comes from either the Primary key or Secondary key "id_scope": "", "device_id": "", - "key": "", + "sas_key": "", # Azure IoT Hub settings - if you are connecting to Azure IoT Hub, fill in this value # To get this value, from the Azure Portal (https://aka.ms/AzurePortalHome), select your IoT Hub, # then select Explorers -> IoT devices, select your device, then copy the entire primary or secondary diff --git a/examples/native_networking/azureiot_central_simpletest.py b/examples/native_networking/azureiot_central_simpletest.py new file mode 100644 index 0000000..9082961 --- /dev/null +++ b/examples/native_networking/azureiot_central_simpletest.py @@ -0,0 +1,111 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import json +import random +import ssl +import time + +import rtc +import socketpool +import wifi + +import adafruit_requests +from adafruit_azureiot import IoTCentralDevice + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +print("Connecting to WiFi...") +wifi.radio.connect(secrets["ssid"], secrets["password"]) + +print("Connected to WiFi!") + +if time.localtime().tm_year < 2022: + print("Setting System Time in UTC") + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + else: + print("Year seems good, skipping set time.") + +# To use Azure IoT Central, you will need to create an IoT Central app. +# You can either create a free tier app that will live for 7 days without an Azure subscription, +# Or a standard tier app that will last for ever with an Azure subscription. +# The standard tiers are free for up to 2 devices +# +# If you don't have an Azure subscription: +# +# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your +# student email address. This will give you $100 of Azure credit and free tiers of a load of +# service, renewable each year you are a student +# +# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30 +# days, as well as free tiers of a load of services +# +# Create an Azure IoT Central app by following these instructions: https://aka.ms/CreateIoTCentralApp +# Add a device template with telemetry, properties and commands, as well as a view to visualize the +# telemetry and execute commands, and a form to set properties. +# +# Next create a device using the device template, and select Connect to get the device connection details. +# Add the connection details to your secrets.py file, using the following values: +# +# 'id_scope' - the devices ID scope +# 'device_id' - the devices device id +# 'key' - the devices primary key +# +# The adafruit-circuitpython-azureiot library depends on the following libraries: +# +# From the Adafruit CircuitPython Bundle (https://github.com/adafruit/Adafruit_CircuitPython_Bundle): +# * adafruit-circuitpython-minimqtt +# * adafruit-circuitpython-requests + + +# Create an IoT Hub device client and connect +esp = None +pool = socketpool.SocketPool(wifi.radio) +device = IoTCentralDevice( + pool, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] +) + +print("Connecting to Azure IoT Central...") +print(dir(device)) +# Connect to IoT Central +device.connect() + +print("Connected to Azure IoT Central!") + +message_counter = 60 + +while True: + try: + # Send telemetry every minute + # You can see the values in the devices dashboard + if message_counter >= 60: + message = {"Temperature": random.randint(0, 50)} + device.send_telemetry(json.dumps(message)) + message_counter = 0 + else: + message_counter += 1 + + # Poll every second for messages from the cloud + device.loop() + except (ValueError, RuntimeError) as e: + print("Connection error, reconnecting\n", str(e)) + wifi.radio.enabled = False + wifi.radio.enabled = True + wifi.radio.connect(secrets["ssid"], secrets["password"]) + device.reconnect() + continue + time.sleep(1) diff --git a/examples/native_networking/azureiot_hub_simpletest.py b/examples/native_networking/azureiot_hub_simpletest.py new file mode 100644 index 0000000..8dad80b --- /dev/null +++ b/examples/native_networking/azureiot_hub_simpletest.py @@ -0,0 +1,106 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import json +import random +import ssl +import time + +import socketpool +import rtc +import wifi + +import adafruit_requests +from adafruit_azureiot import IoTHubDevice + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +print("Connecting to WiFi...") +wifi.radio.connect(secrets["ssid"], secrets["password"]) + +print("Connected to WiFi!") + +if time.localtime().tm_year < 2022: + print("Setting System Time in UTC") + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + else: + print("Year seems good, skipping set time.") + +# You will need an Azure subscription to create an Azure IoT Hub resource +# +# If you don't have an Azure subscription: +# +# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your +# student email address. This will give you $100 of Azure credit and free tiers of a load of +# service, renewable each year you are a student +# +# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30 +# days, as well as free tiers of a load of services +# +# Create an Azure IoT Hub and an IoT device in the Azure portal here: https://aka.ms/AzurePortalHome. +# Instructions to create an IoT Hub and device are here: https://aka.ms/CreateIoTHub +# +# The free tier of IoT Hub allows up to 8,000 messages a day, so try not to send messages too often +# if you are using the free tier +# +# Once you have a hub and a device, copy the device primary connection string. +# Add it to the secrets.py file in an entry called device_connection_string +# +# The adafruit-circuitpython-azureiot library depends on the following libraries: +# +# From the Adafruit CircuitPython Bundle (https://github.com/adafruit/Adafruit_CircuitPython_Bundle): +# * adafruit-circuitpython-minimqtt +# * adafruit-circuitpython-requests + + +esp = None +pool = socketpool.SocketPool(wifi.radio) +# Create an IoT Hub device client and connect +device = IoTHubDevice(pool, esp, secrets["device_connection_string"]) + +print("Connecting to Azure IoT Hub...") + +# Connect to IoT Central +device.connect() + +print("Connected to Azure IoT Hub!") + +message_counter = 60 + +while True: + try: + # Send a device to cloud message every minute + # You can see the overview of messages sent from the device in the Overview tab + # of the IoT Hub in the Azure Portal + if message_counter >= 60: + message = {"Temperature": random.randint(0, 50)} + device.send_device_to_cloud_message(json.dumps(message)) + message_counter = 0 + else: + message_counter += 1 + + # Poll every second for messages from the cloud + device.loop() + except (ValueError, RuntimeError) as e: + print("Connection error, reconnecting\n", str(e)) + # If we lose connectivity, reset the wifi and reconnect + wifi.radio.enabled = False + wifi.radio.enabled = True + wifi.radio.connect(secrets["ssid"], secrets["password"]) + device.reconnect() + continue + time.sleep(1) From 7fd9246dc112fbc61886ea0b1795c27dc0a2de7e Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Sat, 22 Jan 2022 01:23:35 -0800 Subject: [PATCH 02/11] make SAS Key more specific --- README.rst | 2 +- adafruit_azureiot/device_registration.py | 18 +++++------ adafruit_azureiot/hmac.py | 11 +++---- adafruit_azureiot/iot_mqtt.py | 31 ++++++++----------- adafruit_azureiot/iotcentral_device.py | 17 +++++----- adafruit_azureiot/iothub_device.py | 5 +-- adafruit_azureiot/quote.py | 2 +- .../ esp32spi /azureiot_central_commands.py | 2 +- .../azureiot_central_notconnected.py | 2 +- .../ esp32spi /azureiot_central_properties.py | 6 ++-- .../ esp32spi /azureiot_central_simpletest.py | 4 +-- examples/ esp32spi /azureiot_hub_messages.py | 4 +-- .../ esp32spi /azureiot_hub_simpletest.py | 4 +-- .../azureiot_hub_twin_operations.py | 4 +-- examples/azureiot_secrets_example.py | 2 +- .../azureiot_central_simpletest.py | 4 +-- 16 files changed, 47 insertions(+), 71 deletions(-) diff --git a/README.rst b/README.rst index 8fcaf35..02b1eaf 100644 --- a/README.rst +++ b/README.rst @@ -203,7 +203,7 @@ To use Azure IoT Central, you will need to create an Azure IoT Central app, crea from adafruit_azureiot import IoTCentralDevice - device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["sas_key"]) + device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["device_device_sas_key"]) device.connect() Once the device is connected, you will regularly need to run a ``loop`` to poll for messages from the cloud. diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index 13dd0ed..a59a97c 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -49,19 +49,19 @@ def __init__( iface, id_scope: str, device_id: str, - key: str, + device_sas_key: str, logger: Logger = None, ): """Creates an instance of the device registration service :param socket: The network socket :param str id_scope: The ID scope of the device to register :param str device_id: The device ID of the device to register - :param str key: The primary or secondary key of the device to register + :param str device_sas_key: The primary or secondary key of the device to register :param adafruit_logging.Logger logger: The logger to use to log messages """ self._id_scope = id_scope self._device_id = device_id - self._key = key + self._device_sas_key = device_sas_key self._logger = logger if logger is not None else logging.getLogger("log") self._mqtt = None @@ -76,7 +76,7 @@ def __init__( # pylint: disable=C0103 def _on_connect(self, client, userdata, _, rc) -> None: self._logger.info( - f"- device_registration :: _on_connect :: rc = {str(rc)}, userdata = {str(userdata)}" + f"- device_registration :: _on_connect :: rc = {rc}, userdata = {userdata}" ) self._auth_response_received = True @@ -110,7 +110,7 @@ def _connect_to_mqtt(self) -> None: self._mqtt.loop() self._logger.info( - f" - device_registration :: connect :: on_connect must be fired. Connected ? {str(self._mqtt.is_connected())}" + f" - device_registration :: connect :: on_connect must be fired. Connected ? {self._mqtt.is_connected()}" ) if not self._mqtt.is_connected(): @@ -132,7 +132,7 @@ def _start_registration(self) -> None: while self._operation_id is None and retry < 10: time.sleep(1) - retry = retry + 1 + retry += 1 self._mqtt.loop() if self._operation_id is None: @@ -151,7 +151,7 @@ def _wait_for_operation(self) -> None: while self._hostname is None and retry < 10: time.sleep(1) - retry = retry + 1 + retry += 1 self._mqtt.loop() if self._hostname is None: @@ -175,10 +175,10 @@ def register_device(self, expiry: int) -> str: # pylint: disable=C0103 sr = self._id_scope + "%2Fregistrations%2F" + self._device_id sig_no_encode = compute_derived_symmetric_key( - self._key, sr + "\n" + str(expiry) + self._device_sas_key, sr + "\n" + str(expiry) ) sig_encoded = quote(sig_no_encode, "~()*!.'") - auth_string = f"SharedAccessSignature sr={sr}&sig={sig_encoded}&se={str(expiry)}&skn=registration" + auth_string = f"SharedAccessSignature sr={sr}&sig={sig_encoded}&se={expiry}&skn=registration" MQTT.set_socket(self._socket, self._iface) diff --git a/adafruit_azureiot/hmac.py b/adafruit_azureiot/hmac.py index 2f5ef20..24504e3 100644 --- a/adafruit_azureiot/hmac.py +++ b/adafruit_azureiot/hmac.py @@ -331,12 +331,11 @@ def sha_update(sha_info: dict, buffer: Union[bytes, bytearray]) -> None: buffer_idx += i sha_info["local"] += i - if sha_info["local"] == SHA_BLOCKSIZE: - sha_transform(sha_info) - sha_info["local"] = 0 - else: + if sha_info["local"] != SHA_BLOCKSIZE: return + sha_transform(sha_info) + sha_info["local"] = 0 while count >= SHA_BLOCKSIZE: # copy buffer sha_info["data"] = list(buffer[buffer_idx : buffer_idx + SHA_BLOCKSIZE]) @@ -351,9 +350,7 @@ def sha_update(sha_info: dict, buffer: Union[bytes, bytearray]) -> None: def getbuf(s: Union[str, bytes, bytearray]) -> Union[bytes, bytearray]: - if isinstance(s, str): - return s.encode("ascii") - return bytes(s) + return s.encode("ascii") if isinstance(s, str) else bytes(s) def sha_final(sha_info: dict) -> bytes: diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index d2a7d99..47fb622 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -98,17 +98,16 @@ def _gen_sas_token(self) -> str: token_expiry = int(time.time() + self._token_expires) uri = self._hostname + "%2Fdevices%2F" + self._device_id signed_hmac_sha256 = compute_derived_symmetric_key( - self._key, uri + "\n" + str(token_expiry) + self._device_sas_key, uri + "\n" + str(token_expiry) ) signature = quote(signed_hmac_sha256, "~()*!.'") if signature.endswith( "\n" ): # somewhere along the crypto chain a newline is inserted signature = signature[:-1] - token = "SharedAccessSignature sr={}&sig={}&se={}".format( + return "SharedAccessSignature sr={}&sig={}&se={}".format( uri, signature, token_expiry ) - return token def _create_mqtt_client(self) -> None: MQTT.set_socket(self._socket, self._iface) @@ -205,11 +204,7 @@ def _handle_device_twin_update(self, client, topic: str, msg: str) -> None: is_patch = "desired" not in twin - if is_patch: - desired = twin - else: - desired = twin["desired"] - + desired = twin if is_patch else twin["desired"] if "$version" in desired: desired_version = desired["$version"] desired.pop("$version") @@ -295,18 +290,18 @@ def _send_common(self, topic: str, data) -> None: break except RuntimeError as runtime_error: self._logger.info( - "Could not send data, retrying after 0.5 seconds: " - + str(runtime_error) + ( + "Could not send data, retrying after 0.5 seconds: " + + str(runtime_error) + ) ) - retry = retry + 1 + retry += 1 if retry >= 10: self._logger.error("Failed to send data") raise - time.sleep(0.5) continue - gc.collect() def _get_device_settings(self) -> None: @@ -322,7 +317,7 @@ def __init__( iface, hostname: str, device_id: str, - key: str, + device_sas_key: str, token_expires: int = 21600, logger: logging = None, ): @@ -332,7 +327,7 @@ def __init__( :param iface: The network interface to communicate over :param str hostname: The hostname of the MQTT broker to connect to, get this by registering the device :param str device_id: The device ID of the device to register - :param str key: The primary or secondary key of the device to register + :param str device_sas_key: The primary or secondary key of the device to register :param int token_expires: The number of seconds till the token expires, defaults to 6 hours :param adafruit_logging logger: The logger """ @@ -343,7 +338,7 @@ def __init__( self._mqtts = None self._device_id = device_id self._hostname = hostname - self._key = key + self._device_sas_key = device_sas_key self._token_expires = token_expires self._username = "{}/{}/?api-version={}".format( self._hostname, device_id, constants.IOTC_API_VERSION @@ -458,12 +453,12 @@ def send_device_to_cloud_message( if system_properties is not None: firstProp = True - for prop in system_properties: + for prop, value in system_properties.items(): if not firstProp: topic += "&" else: firstProp = False - topic += prop + "=" + str(system_properties[prop]) + topic += prop + "=" + str(value) # Convert message to a string if isinstance(message, dict): diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index 72b5c48..96e0180 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -86,7 +86,7 @@ def __init__( iface, id_scope: str, device_id: str, - key: str, + device_sas_key: str, token_expires: int = 21600, logger: logging = None, ): @@ -95,7 +95,7 @@ def __init__( :param iface: The network interface :param str id_scope: The ID Scope of the device in IoT Central :param str device_id: The device ID of the device in IoT Central - :param str key: The primary or secondary key of the device in IoT Central + :param str device_sas_key: The primary or secondary key of the device in IoT Central :param int token_expires: The number of seconds till the token expires, defaults to 6 hours :param adafruit_logging logger: The logger """ @@ -103,7 +103,7 @@ def __init__( self._iface = iface self._id_scope = id_scope self._device_id = device_id - self._key = key + self._device_sas_key = device_sas_key self._token_expires = token_expires self._logger = logger if logger is not None else logging.getLogger("log") self._device_registration = None @@ -140,7 +140,7 @@ def connect(self) -> None: self._iface, self._id_scope, self._device_id, - self._key, + self._device_sas_key, self._logger, ) @@ -152,14 +152,14 @@ def connect(self) -> None: self._iface, hostname, self._device_id, - self._key, + self._device_sas_key, self._token_expires, self._logger, ) self._logger.debug("Hostname: " + hostname) self._logger.debug("Device Id: " + self._device_id) - self._logger.debug("Shared Access Key: " + self._key) + self._logger.debug("Shared Access Key: " + self._device_sas_key) self._mqtt.connect() self._mqtt.subscribe_to_twins() @@ -185,10 +185,7 @@ def is_connected(self) -> bool: :returns: True if there is an open connection, False if not :rtype: bool """ - if self._mqtt is not None: - return self._mqtt.is_connected() - - return False + return self._mqtt.is_connected() if self._mqtt is not None else False def loop(self) -> None: """Listens for MQTT messages diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index 274b8c4..06288e2 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -336,10 +336,7 @@ def is_connected(self) -> bool: :returns: True if there is an open connection, False if not :rtype: bool """ - if self._mqtt is not None: - return self._mqtt.is_connected() - - return False + return self._mqtt.is_connected() if self._mqtt is not None else False def send_device_to_cloud_message( self, message: Union[str, dict], system_properties: dict = None diff --git a/adafruit_azureiot/quote.py b/adafruit_azureiot/quote.py index 2a5cfbb..4053df5 100644 --- a/adafruit_azureiot/quote.py +++ b/adafruit_azureiot/quote.py @@ -37,7 +37,7 @@ def quote(bytes_val: bytes, safe: Union[str, bytes, bytearray] = "/") -> str: # Normalize 'safe' by converting to bytes and removing non-ASCII chars safe = safe.encode("ascii", "ignore") else: - safe = bytes([char for char in safe if char < 128]) + safe = bytes(char for char in safe if char < 128) if not bytes_val.rstrip(_ALWAYS_SAFE_BYTES + safe): return bytes_val.decode() try: diff --git a/examples/ esp32spi /azureiot_central_commands.py b/examples/ esp32spi /azureiot_central_commands.py index 602c0d8..623d7c6 100644 --- a/examples/ esp32spi /azureiot_central_commands.py +++ b/examples/ esp32spi /azureiot_central_commands.py @@ -99,7 +99,7 @@ # Create an IoT Hub device client and connect device = IoTCentralDevice( - socket, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] + socket, esp, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"] ) # Subscribe to commands diff --git a/examples/ esp32spi /azureiot_central_notconnected.py b/examples/ esp32spi /azureiot_central_notconnected.py index 3853611..e6471cf 100644 --- a/examples/ esp32spi /azureiot_central_notconnected.py +++ b/examples/ esp32spi /azureiot_central_notconnected.py @@ -103,7 +103,7 @@ # Create an IoT Hub device client and connect device = IoTCentralDevice( - socket, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] + socket, esp, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"] ) # don't connect diff --git a/examples/ esp32spi /azureiot_central_properties.py b/examples/ esp32spi /azureiot_central_properties.py index 252f228..36eb0d7 100644 --- a/examples/ esp32spi /azureiot_central_properties.py +++ b/examples/ esp32spi /azureiot_central_properties.py @@ -96,7 +96,7 @@ # Create an IoT Hub device client and connect device = IoTCentralDevice( - socket, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] + socket, esp, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"] ) # Subscribe to property changes @@ -133,16 +133,14 @@ def property_changed(property_name, property_value, version): device.send_property("Desired_Temperature", random.randint(0, 50)) message_counter = 0 else: - message_counter = message_counter + 1 + message_counter += 1 # Poll every second for messages from the cloud device.loop() except (ValueError, RuntimeError) as e: print("Connection error, reconnecting\n", str(e)) - # If we lose connectivity, reset the wifi and reconnect wifi.reset() wifi.connect() device.reconnect() continue - time.sleep(1) diff --git a/examples/ esp32spi /azureiot_central_simpletest.py b/examples/ esp32spi /azureiot_central_simpletest.py index 7999cd3..eee8694 100644 --- a/examples/ esp32spi /azureiot_central_simpletest.py +++ b/examples/ esp32spi /azureiot_central_simpletest.py @@ -97,7 +97,7 @@ # Create an IoT Hub device client and connect device = IoTCentralDevice( - socket, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] + socket, esp, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"] ) print("Connecting to Azure IoT Central...") @@ -118,7 +118,7 @@ device.send_telemetry(json.dumps(message)) message_counter = 0 else: - message_counter = message_counter + 1 + message_counter += 1 # Poll every second for messages from the cloud device.loop() diff --git a/examples/ esp32spi /azureiot_hub_messages.py b/examples/ esp32spi /azureiot_hub_messages.py index 4d98ef3..f558e2a 100644 --- a/examples/ esp32spi /azureiot_hub_messages.py +++ b/examples/ esp32spi /azureiot_hub_messages.py @@ -122,16 +122,14 @@ def cloud_to_device_message_received(body: str, properties: dict): device.send_device_to_cloud_message(json.dumps(message)) message_counter = 0 else: - message_counter = message_counter + 1 + message_counter += 1 # Poll every second for messages from the cloud device.loop() except (ValueError, RuntimeError) as e: print("Connection error, reconnecting\n", str(e)) - # If we lose connectivity, reset the wifi and reconnect wifi.reset() wifi.connect() device.reconnect() continue - time.sleep(1) diff --git a/examples/ esp32spi /azureiot_hub_simpletest.py b/examples/ esp32spi /azureiot_hub_simpletest.py index d6ef4a4..426c5c2 100644 --- a/examples/ esp32spi /azureiot_hub_simpletest.py +++ b/examples/ esp32spi /azureiot_hub_simpletest.py @@ -112,16 +112,14 @@ device.send_device_to_cloud_message(json.dumps(message)) message_counter = 0 else: - message_counter = message_counter + 1 + message_counter += 1 # Poll every second for messages from the cloud device.loop() except (ValueError, RuntimeError) as e: print("Connection error, reconnecting\n", str(e)) - # If we lose connectivity, reset the wifi and reconnect wifi.reset() wifi.connect() device.reconnect() continue - time.sleep(1) diff --git a/examples/ esp32spi /azureiot_hub_twin_operations.py b/examples/ esp32spi /azureiot_hub_twin_operations.py index dacb8ce..c97e29a 100644 --- a/examples/ esp32spi /azureiot_hub_twin_operations.py +++ b/examples/ esp32spi /azureiot_hub_twin_operations.py @@ -134,16 +134,14 @@ def device_twin_desired_updated( device.update_twin(patch) message_counter = 0 else: - message_counter = message_counter + 1 + message_counter += 1 # Poll every second for messages from the cloud device.loop() except (ValueError, RuntimeError) as e: print("Connection error, reconnecting\n", str(e)) - # If we lose connectivity, reset the wifi and reconnect wifi.reset() wifi.connect() device.reconnect() continue - time.sleep(1) diff --git a/examples/azureiot_secrets_example.py b/examples/azureiot_secrets_example.py index 4f2a2e0..26eff42 100644 --- a/examples/azureiot_secrets_example.py +++ b/examples/azureiot_secrets_example.py @@ -27,7 +27,7 @@ # key comes from either the Primary key or Secondary key "id_scope": "", "device_id": "", - "sas_key": "", + "device_sas_key": "", # Azure IoT Hub settings - if you are connecting to Azure IoT Hub, fill in this value # To get this value, from the Azure Portal (https://aka.ms/AzurePortalHome), select your IoT Hub, # then select Explorers -> IoT devices, select your device, then copy the entire primary or secondary diff --git a/examples/native_networking/azureiot_central_simpletest.py b/examples/native_networking/azureiot_central_simpletest.py index 9082961..f54a15d 100644 --- a/examples/native_networking/azureiot_central_simpletest.py +++ b/examples/native_networking/azureiot_central_simpletest.py @@ -76,12 +76,10 @@ esp = None pool = socketpool.SocketPool(wifi.radio) device = IoTCentralDevice( - pool, esp, secrets["id_scope"], secrets["device_id"], secrets["sas_key"] + pool, esp, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"] ) print("Connecting to Azure IoT Central...") -print(dir(device)) -# Connect to IoT Central device.connect() print("Connected to Azure IoT Central!") From c0d8d6206f22e646ae1e614d9ea5fa74016a0901 Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Mon, 24 Jan 2022 19:38:31 -0800 Subject: [PATCH 03/11] typo in the README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 02b1eaf..f375db1 100644 --- a/README.rst +++ b/README.rst @@ -203,7 +203,7 @@ To use Azure IoT Central, you will need to create an Azure IoT Central app, crea from adafruit_azureiot import IoTCentralDevice - device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["device_device_sas_key"]) + device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"]) device.connect() Once the device is connected, you will regularly need to run a ``loop`` to poll for messages from the cloud. From 1f6bec3e591c6e3740d2c041310d2030a73b11ad Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Mon, 24 Jan 2022 21:12:37 -0800 Subject: [PATCH 04/11] Update samples and docs --- .gitignore | 2 +- README.rst | 24 +++- docs/examples.rst | 90 ++++++++++--- .../ esp32spi /azureiot_central_commands.py | 2 +- .../azureiot_central_notconnected.py | 2 +- .../ esp32spi /azureiot_central_properties.py | 2 +- .../ esp32spi /azureiot_central_simpletest.py | 2 +- .../azureiot_central_commands.py | 115 ++++++++++++++++ .../azureiot_central_notconnected.py | 92 +++++++++++++ .../azureiot_central_properties.py | 123 ++++++++++++++++++ .../azureiot_central_simpletest.py | 2 +- .../azureiot_hub_directmethods.py | 107 +++++++++++++++ .../azureiot_hub_messages.py | 114 ++++++++++++++++ .../azureiot_hub_simpletest.py | 1 + .../azureiot_hub_twin_operations.py | 123 ++++++++++++++++++ 15 files changed, 775 insertions(+), 26 deletions(-) create mode 100644 examples/native_networking/azureiot_central_commands.py create mode 100644 examples/native_networking/azureiot_central_notconnected.py create mode 100644 examples/native_networking/azureiot_central_properties.py create mode 100644 examples/native_networking/azureiot_hub_directmethods.py create mode 100644 examples/native_networking/azureiot_hub_messages.py create mode 100644 examples/native_networking/azureiot_hub_twin_operations.py diff --git a/.gitignore b/.gitignore index fadf338..6d34d93 100644 --- a/.gitignore +++ b/.gitignore @@ -107,7 +107,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. diff --git a/README.rst b/README.rst index f375db1..c5a0479 100644 --- a/README.rst +++ b/README.rst @@ -63,6 +63,9 @@ To create an Azure IoT Hub instance or an Azure IoT Central app, you will need a - If you are not a student, head to `aka.ms/FreeAz `_ and sign up to get $200 of credit for 30 days, as well as free tiers of a load of services. You will need a credit card for validation only, your card will not be charged. +ESP32SPI Networking +=================== + To use this library, you will need to create an ESP32_SPI WifiManager, connected to WiFi. You will also need to set the current time, as this is used to generate time-based authentication keys. One way to do this is via the `Adafruit CircuitPython NTP `_ library with the following code: .. code-block:: python @@ -74,6 +77,23 @@ To use this library, you will need to create an ESP32_SPI WifiManager, connected time.sleep(5) ntp.set_time() +Native Networking +================= +To use this library, with boards that have native networking support, you need to be connected to a network. You will also need to set the current time, as this is used to generate time-based authentication keys. One way to do this is by using the `Adafruit IoT Time Service `_ via the `Requests library _` with the following code: + +.. code-block:: python + + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + Azure IoT Hub ------------- @@ -172,7 +192,7 @@ To use Azure IoT Central, you will need to create an Azure IoT Central app, crea - Head to `Azure IoT Central `__ - Follow the instructions in the `Microsoft Docs `__ to create an application. Every tier is free for up to 2 devices. - Follow the instructions in the `Microsoft Docs `__ to create a device template. -- Create a device based off the template, and select **Connect** to get the device connection details. Store the ID Scope, Device ID and either the Primary or secondary Key in your ``secrets.py`` file. +- Create a device based off the template, and select **Connect** to get the device connection details. Store the ID Scope, Device ID and either the primary or secondary device SAS key in your ``secrets.py`` file. .. image:: iot-central-connect-button.png :alt: The IoT Central connect button @@ -194,7 +214,7 @@ To use Azure IoT Central, you will need to create an Azure IoT Central app, crea # Azure IoT Central settings "id_scope": "", "device_id": "", - "key": "" + "device_sas_key": "" } **Connect your device to your Azure IoT Central app** diff --git a/docs/examples.rst b/docs/examples.rst index 217c5ec..73d42b2 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,53 +1,107 @@ -IoT Hub +IoT Hub ESP32SPI Networking ------------ Ensure your IoT Hub device works with this simple test. -.. literalinclude:: ../examples/azureiot_hub_simpletest.py - :caption: examples/azureiot_hub_simpletest.py +.. literalinclude:: ../examples/esp32spi/azureiot_hub_simpletest.py + :caption: examples/esp32spi/azureiot_hub_simpletest.py :linenos: Handle direct methods. -.. literalinclude:: ../examples/azureiot_hub_directmethods.py - :caption: examples/azureiot_hub_directmethods.py +.. literalinclude:: ../examples/esp32spi/azureiot_hub_directmethods.py + :caption: examples/esp32spi/azureiot_hub_directmethods.py :linenos: Send device to cloud messages, and handle cloud to device messages. -.. literalinclude:: ../examples/azureiot_hub_messages.py - :caption: examples/azureiot_hub_messages.py +.. literalinclude:: ../examples/esp32spi/azureiot_hub_messages.py + :caption: examples/esp32spi/azureiot_hub_messages.py :linenos: Update the reported properties of the devices device twin, and receive updates to desired properties. -.. literalinclude:: ../examples/azureiot_hub_twin_operations.py - :caption: examples/azureiot_hub_twin_operations.py +.. literalinclude:: ../examples/esp32spi/azureiot_hub_twin_operations.py + :caption: examples/esp32spi/azureiot_hub_twin_operations.py :linenos: -IoT Central +IoT Central ESP32SPI Networking ------------ Ensure your IoT Central device works with this simple test. -.. literalinclude:: ../examples/azureiot_central_simpletest.py - :caption: examples/azureiot_central_simpletest.py +.. literalinclude:: ../examples/esp32spi/azureiot_central_simpletest.py + :caption: examples/esp32spi/azureiot_central_simpletest.py :linenos: Handle commands. -.. literalinclude:: ../examples/azureiot_central_commands.py - :caption: examples/azureiot_central_commands.py +.. literalinclude:: ../examples/esp32spi/azureiot_central_commands.py + :caption: examples/esp32spi/azureiot_central_commands.py :linenos: Update the properties of the device, and receive updates to properties. -.. literalinclude:: ../examples/azureiot_central_properties.py - :caption: examples/azureiot_central_properties.py +.. literalinclude:: ../examples/esp32spi/azureiot_central_properties.py + :caption: examples/esp32spi/azureiot_central_properties.py :linenos: Handle connection errors. -.. literalinclude:: ../examples/azureiot_central_notconnected.py - :caption: examples/azureiot_central_notconnected.py +.. literalinclude:: ../examples/esp32spi/azureiot_central_notconnected.py + :caption: examples/esp32spi/azureiot_central_notconnected.py + :linenos: + +IoT Hub Native Networking +------------ + +Ensure your IoT Hub device works with this simple test. + +.. literalinclude:: ../examples/native_networking/azureiot_hub_simpletest.py + :caption: examples/native_networking/azureiot_hub_simpletest.py + :linenos: + +Handle direct methods. + +.. literalinclude:: ../examples/native_networking/azureiot_hub_directmethods.py + :caption: examples/native_networking/azureiot_hub_directmethods.py + :linenos: + +Send device to cloud messages, and handle cloud to device messages. + +.. literalinclude:: ../examples/native_networking/azureiot_hub_messages.py + :caption: examples/native_networking/azureiot_hub_messages.py + :linenos: + +Update the reported properties of the devices device twin, and receive updates to desired properties. + +.. literalinclude:: ../examples/native_networking/azureiot_hub_twin_operations.py + :caption: examples/native_networking/azureiot_hub_twin_operations.py + :linenos: + +IoT Central Native Networking +------------ + +Ensure your IoT Central device works with this simple test. + +.. literalinclude:: ../examples/native_networking/azureiot_central_simpletest.py + :caption: examples/native_networking/azureiot_central_simpletest.py + :linenos: + +Handle commands. + +.. literalinclude:: ../examples/native_networking/azureiot_central_commands.py + :caption: examples/native_networking/azureiot_central_commands.py + :linenos: + +Update the properties of the device, and receive updates to properties. + +.. literalinclude:: ../examples/native_networking/azureiot_central_properties.py + :caption: examples/native_networking/azureiot_central_properties.py + :linenos: + +Handle connection errors. + +.. literalinclude:: ../examples/native_networking/azureiot_central_notconnected.py + :caption: examples/native_networking/azureiot_central_notconnected.py :linenos: diff --git a/examples/ esp32spi /azureiot_central_commands.py b/examples/ esp32spi /azureiot_central_commands.py index 623d7c6..76b2c1a 100644 --- a/examples/ esp32spi /azureiot_central_commands.py +++ b/examples/ esp32spi /azureiot_central_commands.py @@ -84,7 +84,7 @@ # # 'id_scope' - the devices ID scope # 'device_id' - the devices device id -# 'key' - the devices primary key +# 'device_sas_key' - the devices primary key # # The adafruit-circuitpython-azureiot library depends on the following libraries: # diff --git a/examples/ esp32spi /azureiot_central_notconnected.py b/examples/ esp32spi /azureiot_central_notconnected.py index e6471cf..beec69b 100644 --- a/examples/ esp32spi /azureiot_central_notconnected.py +++ b/examples/ esp32spi /azureiot_central_notconnected.py @@ -86,7 +86,7 @@ # # 'id_scope' - the devices ID scope # 'device_id' - the devices device id -# 'key' - the devices primary key +# 'device_sas_key' - the devices primary key # # The adafruit-circuitpython-azureiot library depends on the following libraries: # diff --git a/examples/ esp32spi /azureiot_central_properties.py b/examples/ esp32spi /azureiot_central_properties.py index 36eb0d7..464954b 100644 --- a/examples/ esp32spi /azureiot_central_properties.py +++ b/examples/ esp32spi /azureiot_central_properties.py @@ -85,7 +85,7 @@ # # 'id_scope' - the devices ID scope # 'device_id' - the devices device id -# 'key' - the devices primary key +# 'device_sas_key' - the devices primary key # # The adafruit-circuitpython-azureiot library depends on the following libraries: # diff --git a/examples/ esp32spi /azureiot_central_simpletest.py b/examples/ esp32spi /azureiot_central_simpletest.py index eee8694..3821638 100644 --- a/examples/ esp32spi /azureiot_central_simpletest.py +++ b/examples/ esp32spi /azureiot_central_simpletest.py @@ -86,7 +86,7 @@ # # 'id_scope' - the devices ID scope # 'device_id' - the devices device id -# 'key' - the devices primary key +# 'device_sas_key' - the devices primary key # # The adafruit-circuitpython-azureiot library depends on the following libraries: # diff --git a/examples/native_networking/azureiot_central_commands.py b/examples/native_networking/azureiot_central_commands.py new file mode 100644 index 0000000..073d1c9 --- /dev/null +++ b/examples/native_networking/azureiot_central_commands.py @@ -0,0 +1,115 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import ssl +import time + +import rtc +import socketpool +import wifi + +import adafruit_requests +from adafruit_azureiot import IoTCentralDevice +from adafruit_azureiot.iot_mqtt import IoTResponse + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +print("Connecting to WiFi...") +wifi.radio.connect(secrets["ssid"], secrets["password"]) + +print("Connected to WiFi!") + +if time.localtime().tm_year < 2022: + print("Setting System Time in UTC") + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + else: + print("Year seems good, skipping set time.") + +# To use Azure IoT Central, you will need to create an IoT Central app. +# You can either create a free tier app that will live for 7 days without an Azure subscription, +# Or a standard tier app that will last for ever with an Azure subscription. +# The standard tiers are free for up to 2 devices +# +# If you don't have an Azure subscription: +# +# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your +# student email address. This will give you $100 of Azure credit and free tiers of a load of +# service, renewable each year you are a student +# +# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30 +# days, as well as free tiers of a load of services +# +# Create an Azure IoT Central app by following these instructions: https://aka.ms/CreateIoTCentralApp +# Add a device template with telemetry, properties and commands, as well as a view to visualize the +# telemetry and execute commands, and a form to set properties. +# +# Next create a device using the device template, and select Connect to get the device connection details. +# Add the connection details to your secrets.py file, using the following values: +# +# 'id_scope' - the devices ID scope +# 'device_id' - the devices device id +# 'device_sas_key' - the devices primary key +# +# The adafruit-circuitpython-azureiot library depends on the following libraries: +# +# From the Adafruit CircuitPython Bundle (https://github.com/adafruit/Adafruit_CircuitPython_Bundle): +# * adafruit-circuitpython-minimqtt +# * adafruit-circuitpython-requests + + +# Create an IoT Hub device client and connect +esp = None +pool = socketpool.SocketPool(wifi.radio) +device = IoTCentralDevice( + pool, esp, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"] +) + +# Subscribe to commands +# Commands can be sent from the devices Dashboard in IoT Central, assuming +# the device template and view has been set up with the commands +# Command handlers need to return a response to show if the command was handled +# successfully or not, returning an HTTP status code and message +def command_executed(command_name: str, payload) -> IoTResponse: + print("Command", command_name, "executed with payload", str(payload)) + # return a status code and message to indicate if the command was handled correctly + return IoTResponse(200, "OK") + + +# Subscribe to the command execute event +device.on_command_executed = command_executed + + +print("Connecting to Azure IoT Central...") +device.connect() + +print("Connected to Azure IoT Central!") + +message_counter = 60 + +while True: + try: + # Poll every second for messages from the cloud + device.loop() + except (ValueError, RuntimeError) as e: + print("Connection error, reconnecting\n", str(e)) + # If we lose connectivity, reset the wifi and reconnect + wifi.reset() + wifi.connect() + device.reconnect() + continue + + time.sleep(1) diff --git a/examples/native_networking/azureiot_central_notconnected.py b/examples/native_networking/azureiot_central_notconnected.py new file mode 100644 index 0000000..f601161 --- /dev/null +++ b/examples/native_networking/azureiot_central_notconnected.py @@ -0,0 +1,92 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import json +import random +import ssl +import time + +import rtc +import socketpool +import wifi + +import adafruit_requests +from adafruit_azureiot import ( + IoTCentralDevice, + IoTError, +) + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +print("Connecting to WiFi...") +wifi.radio.connect(secrets["ssid"], secrets["password"]) + +print("Connected to WiFi!") + +if time.localtime().tm_year < 2022: + print("Setting System Time in UTC") + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + else: + print("Year seems good, skipping set time.") + +# To use Azure IoT Central, you will need to create an IoT Central app. +# You can either create a free tier app that will live for 7 days without an Azure subscription, +# Or a standard tier app that will last for ever with an Azure subscription. +# The standard tiers are free for up to 2 devices +# +# If you don't have an Azure subscription: +# +# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your +# student email address. This will give you $100 of Azure credit and free tiers of a load of +# service, renewable each year you are a student +# +# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30 +# days, as well as free tiers of a load of services +# +# Create an Azure IoT Central app by following these instructions: https://aka.ms/CreateIoTCentralApp +# Add a device template with telemetry, properties and commands, as well as a view to visualize the +# telemetry and execute commands, and a form to set properties. +# +# Next create a device using the device template, and select Connect to get the device connection details. +# Add the connection details to your secrets.py file, using the following values: +# +# 'id_scope' - the devices ID scope +# 'device_id' - the devices device id +# 'device_sas_key' - the devices primary key +# +# The adafruit-circuitpython-azureiot library depends on the following libraries: +# +# From the Adafruit CircuitPython Bundle (https://github.com/adafruit/Adafruit_CircuitPython_Bundle): +# * adafruit-circuitpython-minimqtt +# * adafruit-circuitpython-requests + + +# Create an IoT Hub device client and connect +esp = None +pool = socketpool.SocketPool(wifi.radio) +device = IoTCentralDevice( + pool, esp, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"] +) + +# don't connect +# device.connect() + +try: + message = {"Temperature": random.randint(0, 50)} + device.send_telemetry(json.dumps(message)) +except IoTError as iot_error: + print("Error - ", iot_error.message) diff --git a/examples/native_networking/azureiot_central_properties.py b/examples/native_networking/azureiot_central_properties.py new file mode 100644 index 0000000..6c97c6a --- /dev/null +++ b/examples/native_networking/azureiot_central_properties.py @@ -0,0 +1,123 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import random +import ssl +import time + +import rtc +import socketpool +import wifi + +import adafruit_requests +from adafruit_azureiot import IoTCentralDevice + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +print("Connecting to WiFi...") +wifi.radio.connect(secrets["ssid"], secrets["password"]) + +print("Connected to WiFi!") + +if time.localtime().tm_year < 2022: + print("Setting System Time in UTC") + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + else: + print("Year seems good, skipping set time.") + +# To use Azure IoT Central, you will need to create an IoT Central app. +# You can either create a free tier app that will live for 7 days without an Azure subscription, +# Or a standard tier app that will last for ever with an Azure subscription. +# The standard tiers are free for up to 2 devices +# +# If you don't have an Azure subscription: +# +# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your +# student email address. This will give you $100 of Azure credit and free tiers of a load of +# service, renewable each year you are a student +# +# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30 +# days, as well as free tiers of a load of services +# +# Create an Azure IoT Central app by following these instructions: https://aka.ms/CreateIoTCentralApp +# Add a device template with telemetry, properties and commands, as well as a view to visualize the +# telemetry and execute commands, and a form to set properties. +# +# Next create a device using the device template, and select Connect to get the device connection details. +# Add the connection details to your secrets.py file, using the following values: +# +# 'id_scope' - the devices ID scope +# 'device_id' - the devices device id +# 'device_sas_key' - the devices primary key +# +# The adafruit-circuitpython-azureiot library depends on the following libraries: +# +# From the Adafruit CircuitPython Bundle (https://github.com/adafruit/Adafruit_CircuitPython_Bundle): +# * adafruit-circuitpython-minimqtt +# * adafruit-circuitpython-requests + + +# Create an IoT Hub device client and connect +esp = None +pool = socketpool.SocketPool(wifi.radio) +device = IoTCentralDevice( + pool, esp, secrets["id_scope"], secrets["device_id"], secrets["device_sas_key"] +) + +# Subscribe to property changes +# Properties can be updated either in code, or by adding a form to the view +# in the device template, and setting the value on the dashboard for the device +def property_changed(property_name, property_value, version): + print( + "Property", + property_name, + "updated to", + str(property_value), + "version", + str(version), + ) + + +# Subscribe to the property changed event +device.on_property_changed = property_changed + +print("Connecting to Azure IoT Central...") +device.connect() + +print("Connected to Azure IoT Central!") + +message_counter = 60 + +while True: + try: + # Send property values every minute + # You can see the values in the devices dashboard + if message_counter >= 60: + device.send_property("Desired_Temperature", random.randint(0, 50)) + message_counter = 0 + else: + message_counter += 1 + + # Poll every second for messages from the cloud + device.loop() + except (ValueError, RuntimeError) as e: + print("Connection error, reconnecting\n", str(e)) + wifi.reset() + wifi.connect() + device.reconnect() + continue + time.sleep(1) diff --git a/examples/native_networking/azureiot_central_simpletest.py b/examples/native_networking/azureiot_central_simpletest.py index f54a15d..cfa1632 100644 --- a/examples/native_networking/azureiot_central_simpletest.py +++ b/examples/native_networking/azureiot_central_simpletest.py @@ -63,7 +63,7 @@ # # 'id_scope' - the devices ID scope # 'device_id' - the devices device id -# 'key' - the devices primary key +# 'device_sas_key' - the devices primary key # # The adafruit-circuitpython-azureiot library depends on the following libraries: # diff --git a/examples/native_networking/azureiot_hub_directmethods.py b/examples/native_networking/azureiot_hub_directmethods.py new file mode 100644 index 0000000..5edf67d --- /dev/null +++ b/examples/native_networking/azureiot_hub_directmethods.py @@ -0,0 +1,107 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import ssl +import time + +import socketpool +import rtc +import wifi + +import adafruit_requests +from adafruit_azureiot import IoTHubDevice +from adafruit_azureiot.iot_mqtt import IoTResponse + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +print("Connecting to WiFi...") +wifi.radio.connect(secrets["ssid"], secrets["password"]) + +print("Connected to WiFi!") + +if time.localtime().tm_year < 2022: + print("Setting System Time in UTC") + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + else: + print("Year seems good, skipping set time.") + +# You will need an Azure subscription to create an Azure IoT Hub resource +# +# If you don't have an Azure subscription: +# +# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your +# student email address. This will give you $100 of Azure credit and free tiers of a load of +# service, renewable each year you are a student +# +# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30 +# days, as well as free tiers of a load of services +# +# Create an Azure IoT Hub and an IoT device in the Azure portal here: https://aka.ms/AzurePortalHome. +# Instructions to create an IoT Hub and device are here: https://aka.ms/CreateIoTHub +# +# The free tier of IoT Hub allows up to 8,000 messages a day, so try not to send messages too often +# if you are using the free tier +# +# Once you have a hub and a device, copy the device primary connection string. +# Add it to the secrets.py file in an entry called device_connection_string +# +# The adafruit-circuitpython-azureiot library depends on the following libraries: +# +# From the Adafruit CircuitPython Bundle (https://github.com/adafruit/Adafruit_CircuitPython_Bundle): +# * adafruit-circuitpython-minimqtt +# * adafruit-circuitpython-requests + + +esp = None +pool = socketpool.SocketPool(wifi.radio) +# Create an IoT Hub device client and connect +device = IoTHubDevice(pool, esp, secrets["device_connection_string"]) + +# Subscribe to direct method calls +# To invoke a method on the device, select it in the Azure Portal, select Direct Method, +# fill in the method name and payload, then select Invoke Method +# Direct method handlers need to return a response to show if the method was handled +# successfully or not, returning an HTTP status code and message +def direct_method_invoked(method_name: str, payload) -> IoTResponse: + print("Received direct method", method_name, "with data", str(payload)) + # return a status code and message to indicate if the direct method was handled correctly + return IoTResponse(200, "OK") + + +# Subscribe to the direct method invoked event +device.on_direct_method_invoked = direct_method_invoked +print("Connecting to Azure IoT Hub...") + +# Connect to IoT Central +device.connect() + +print("Connected to Azure IoT Hub!") + +while True: + try: + # Poll every second for messages from the cloud + device.loop() + except (ValueError, RuntimeError) as e: + print("Connection error, reconnecting\n", str(e)) + # If we lose connectivity, reset the wifi and reconnect + wifi.radio.enabled = False + wifi.radio.enabled = True + wifi.radio.connect(secrets["ssid"], secrets["password"]) + device.reconnect() + continue + + time.sleep(1) diff --git a/examples/native_networking/azureiot_hub_messages.py b/examples/native_networking/azureiot_hub_messages.py new file mode 100644 index 0000000..0ab941b --- /dev/null +++ b/examples/native_networking/azureiot_hub_messages.py @@ -0,0 +1,114 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import json +import random +import ssl +import time + +import socketpool +import rtc +import wifi + +import adafruit_requests +from adafruit_azureiot import IoTHubDevice + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +print("Connecting to WiFi...") +wifi.radio.connect(secrets["ssid"], secrets["password"]) + +print("Connected to WiFi!") + +if time.localtime().tm_year < 2022: + print("Setting System Time in UTC") + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + else: + print("Year seems good, skipping set time.") + +# You will need an Azure subscription to create an Azure IoT Hub resource +# +# If you don't have an Azure subscription: +# +# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your +# student email address. This will give you $100 of Azure credit and free tiers of a load of +# service, renewable each year you are a student +# +# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30 +# days, as well as free tiers of a load of services +# +# Create an Azure IoT Hub and an IoT device in the Azure portal here: https://aka.ms/AzurePortalHome. +# Instructions to create an IoT Hub and device are here: https://aka.ms/CreateIoTHub +# +# The free tier of IoT Hub allows up to 8,000 messages a day, so try not to send messages too often +# if you are using the free tier +# +# Once you have a hub and a device, copy the device primary connection string. +# Add it to the secrets.py file in an entry called device_connection_string +# +# The adafruit-circuitpython-azureiot library depends on the following libraries: +# +# From the Adafruit CircuitPython Bundle (https://github.com/adafruit/Adafruit_CircuitPython_Bundle): +# * adafruit-circuitpython-minimqtt +# * adafruit-circuitpython-requests + + +esp = None +pool = socketpool.SocketPool(wifi.radio) +# Create an IoT Hub device client and connect +device = IoTHubDevice(pool, esp, secrets["device_connection_string"]) + +# Subscribe to cloud to device messages +# To send a message to the device, select it in the Azure Portal, select Message To Device, +# fill in the message and any properties you want to add, then select Send Message +def cloud_to_device_message_received(body: str, properties: dict): + print("Received message with body", body, "and properties", json.dumps(properties)) + + +# Subscribe to the cloud to device message received events +device.on_cloud_to_device_message_received = cloud_to_device_message_received + +print("Connecting to Azure IoT Hub...") +device.connect() + +print("Connected to Azure IoT Hub!") + +message_counter = 60 + +while True: + try: + # Send a device to cloud message every minute + # You can see the overview of messages sent from the device in the Overview tab + # of the IoT Hub in the Azure Portal + if message_counter >= 60: + message = {"Temperature": random.randint(0, 50)} + device.send_device_to_cloud_message(json.dumps(message)) + message_counter = 0 + else: + message_counter += 1 + + # Poll every second for messages from the cloud + device.loop() + except (ValueError, RuntimeError) as e: + print("Connection error, reconnecting\n", str(e)) + # If we lose connectivity, reset the wifi and reconnect + wifi.radio.enabled = False + wifi.radio.enabled = True + wifi.radio.connect(secrets["ssid"], secrets["password"]) + device.reconnect() + continue + time.sleep(1) diff --git a/examples/native_networking/azureiot_hub_simpletest.py b/examples/native_networking/azureiot_hub_simpletest.py index 8dad80b..57dc741 100644 --- a/examples/native_networking/azureiot_hub_simpletest.py +++ b/examples/native_networking/azureiot_hub_simpletest.py @@ -71,6 +71,7 @@ pool = socketpool.SocketPool(wifi.radio) # Create an IoT Hub device client and connect device = IoTHubDevice(pool, esp, secrets["device_connection_string"]) +print(dir(device)) print("Connecting to Azure IoT Hub...") diff --git a/examples/native_networking/azureiot_hub_twin_operations.py b/examples/native_networking/azureiot_hub_twin_operations.py new file mode 100644 index 0000000..6c9b715 --- /dev/null +++ b/examples/native_networking/azureiot_hub_twin_operations.py @@ -0,0 +1,123 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import random +import ssl +import time + +import socketpool +import rtc +import wifi + +import adafruit_requests +from adafruit_azureiot import IoTHubDevice + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +print("Connecting to WiFi...") +wifi.radio.connect(secrets["ssid"], secrets["password"]) + +print("Connected to WiFi!") + +if time.localtime().tm_year < 2022: + print("Setting System Time in UTC") + pool = socketpool.SocketPool(wifi.radio) + requests = adafruit_requests.Session(pool, ssl.create_default_context()) + response = requests.get("https://io.adafruit.com/api/v2/time/seconds") + if response: + if response.status_code == 200: + r = rtc.RTC() + r.datetime = time.localtime(int(response.text)) + print(f"System Time: {r.datetime}") + else: + print("Setting time failed") + else: + print("Year seems good, skipping set time.") + +# You will need an Azure subscription to create an Azure IoT Hub resource +# +# If you don't have an Azure subscription: +# +# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your +# student email address. This will give you $100 of Azure credit and free tiers of a load of +# service, renewable each year you are a student +# +# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30 +# days, as well as free tiers of a load of services +# +# Create an Azure IoT Hub and an IoT device in the Azure portal here: https://aka.ms/AzurePortalHome. +# Instructions to create an IoT Hub and device are here: https://aka.ms/CreateIoTHub +# +# The free tier of IoT Hub allows up to 8,000 messages a day, so try not to send messages too often +# if you are using the free tier +# +# Once you have a hub and a device, copy the device primary connection string. +# Add it to the secrets.py file in an entry called device_connection_string +# +# The adafruit-circuitpython-azureiot library depends on the following libraries: +# +# From the Adafruit CircuitPython Bundle (https://github.com/adafruit/Adafruit_CircuitPython_Bundle): +# * adafruit-circuitpython-minimqtt +# * adafruit-circuitpython-requests + + +esp = None +pool = socketpool.SocketPool(wifi.radio) +# Create an IoT Hub device client and connect +device = IoTHubDevice(pool, esp, secrets["device_connection_string"]) + +# Subscribe to device twin desired property updates +# To see these changes, update the desired properties for the device either in code +# or in the Azure portal by selecting the device in the IoT Hub blade, selecting +# Device Twin then adding or amending an entry in the 'desired' section +def device_twin_desired_updated( + desired_property_name: str, desired_property_value, desired_version: int +): + print( + "Property", + desired_property_name, + "updated to", + str(desired_property_value), + "version", + desired_version, + ) + + +# Subscribe to the device twin desired property updated event +device.on_device_twin_desired_updated = device_twin_desired_updated + +print("Connecting to Azure IoT Hub...") +device.connect() + +print("Connected to Azure IoT Hub!") + +message_counter = 60 + +while True: + try: + if message_counter >= 60: + # Send a reported property twin update every minute + # You can see these in the portal by selecting the device in the IoT Hub blade, selecting + # Device Twin then looking for the updates in the 'reported' section + patch = {"Temperature": random.randint(0, 50)} + device.update_twin(patch) + message_counter = 0 + else: + message_counter += 1 + + # Poll every second for messages from the cloud + device.loop() + except (ValueError, RuntimeError) as e: + print("Connection error, reconnecting\n", str(e)) + # If we lose connectivity, reset the wifi and reconnect + wifi.radio.enabled = False + wifi.radio.enabled = True + wifi.radio.connect(secrets["ssid"], secrets["password"]) + device.reconnect() + continue + time.sleep(1) From ab27b6e8a87c8efce868a0d3591218f5d223d1b5 Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Mon, 24 Jan 2022 22:47:47 -0800 Subject: [PATCH 05/11] Update Dependencies DOCSTRING --- adafruit_azureiot/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/adafruit_azureiot/__init__.py b/adafruit_azureiot/__init__.py index 692fcb0..5cca8fd 100644 --- a/adafruit_azureiot/__init__.py +++ b/adafruit_azureiot/__init__.py @@ -19,8 +19,16 @@ * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases +**With ESP32SPI Peripheral Networking** + * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice * Adafruit's ESP32SPI library: https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI +* Adafruit's NTP library: https://github.com/adafruit/Adafruit_CircuitPython_NTP + +**With Native Networking** + +* CircuitPython's Wifi Module: https://docs.circuitpython.org/en/latest/shared-bindings/wifi/index.html +* Adafruit's Requests Library: https://github.com/adafruit/Adafruit_CircuitPython_Requests/ """ from .iot_error import IoTError From 94d9439f9ab24fd529a0389a1bce30ec15f5007d Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Mon, 24 Jan 2022 23:39:33 -0800 Subject: [PATCH 06/11] Doc Fixes --- README.rst | 2 +- docs/examples.rst | 8 ++++---- .../{ esp32spi => esp32spi}/azureiot_central_commands.py | 0 .../azureiot_central_notconnected.py | 0 .../azureiot_central_properties.py | 0 .../azureiot_central_simpletest.py | 0 .../azureiot_hub_directmethods.py | 0 .../{ esp32spi => esp32spi}/azureiot_hub_messages.py | 0 .../{ esp32spi => esp32spi}/azureiot_hub_simpletest.py | 0 .../azureiot_hub_twin_operations.py | 0 10 files changed, 5 insertions(+), 5 deletions(-) rename examples/{ esp32spi => esp32spi}/azureiot_central_commands.py (100%) rename examples/{ esp32spi => esp32spi}/azureiot_central_notconnected.py (100%) rename examples/{ esp32spi => esp32spi}/azureiot_central_properties.py (100%) rename examples/{ esp32spi => esp32spi}/azureiot_central_simpletest.py (100%) rename examples/{ esp32spi => esp32spi}/azureiot_hub_directmethods.py (100%) rename examples/{ esp32spi => esp32spi}/azureiot_hub_messages.py (100%) rename examples/{ esp32spi => esp32spi}/azureiot_hub_simpletest.py (100%) rename examples/{ esp32spi => esp32spi}/azureiot_hub_twin_operations.py (100%) diff --git a/README.rst b/README.rst index 4ede2db..ad85487 100644 --- a/README.rst +++ b/README.rst @@ -79,7 +79,7 @@ To use this library, you will need to create an ESP32_SPI WifiManager, connected Native Networking ================= -To use this library, with boards that have native networking support, you need to be connected to a network. You will also need to set the current time, as this is used to generate time-based authentication keys. One way to do this is by using the `Adafruit IoT Time Service `_ via the `Requests library _` with the following code: +To use this library, with boards that have native networking support, you need to be connected to a network. You will also need to set the current time, as this is used to generate time-based authentication keys. One way to do this is by using the `Adafruit IoT Time Service `_ via the `Adafruit Requests library `_ with the following code: .. code-block:: python diff --git a/docs/examples.rst b/docs/examples.rst index 73d42b2..2790cfd 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,5 +1,5 @@ IoT Hub ESP32SPI Networking ------------- +--------------------------- Ensure your IoT Hub device works with this simple test. @@ -26,7 +26,7 @@ Update the reported properties of the devices device twin, and receive updates t :linenos: IoT Central ESP32SPI Networking ------------- +------------------------------- Ensure your IoT Central device works with this simple test. @@ -53,7 +53,7 @@ Handle connection errors. :linenos: IoT Hub Native Networking ------------- +------------------------- Ensure your IoT Hub device works with this simple test. @@ -80,7 +80,7 @@ Update the reported properties of the devices device twin, and receive updates t :linenos: IoT Central Native Networking ------------- +----------------------------- Ensure your IoT Central device works with this simple test. diff --git a/examples/ esp32spi /azureiot_central_commands.py b/examples/esp32spi/azureiot_central_commands.py similarity index 100% rename from examples/ esp32spi /azureiot_central_commands.py rename to examples/esp32spi/azureiot_central_commands.py diff --git a/examples/ esp32spi /azureiot_central_notconnected.py b/examples/esp32spi/azureiot_central_notconnected.py similarity index 100% rename from examples/ esp32spi /azureiot_central_notconnected.py rename to examples/esp32spi/azureiot_central_notconnected.py diff --git a/examples/ esp32spi /azureiot_central_properties.py b/examples/esp32spi/azureiot_central_properties.py similarity index 100% rename from examples/ esp32spi /azureiot_central_properties.py rename to examples/esp32spi/azureiot_central_properties.py diff --git a/examples/ esp32spi /azureiot_central_simpletest.py b/examples/esp32spi/azureiot_central_simpletest.py similarity index 100% rename from examples/ esp32spi /azureiot_central_simpletest.py rename to examples/esp32spi/azureiot_central_simpletest.py diff --git a/examples/ esp32spi /azureiot_hub_directmethods.py b/examples/esp32spi/azureiot_hub_directmethods.py similarity index 100% rename from examples/ esp32spi /azureiot_hub_directmethods.py rename to examples/esp32spi/azureiot_hub_directmethods.py diff --git a/examples/ esp32spi /azureiot_hub_messages.py b/examples/esp32spi/azureiot_hub_messages.py similarity index 100% rename from examples/ esp32spi /azureiot_hub_messages.py rename to examples/esp32spi/azureiot_hub_messages.py diff --git a/examples/ esp32spi /azureiot_hub_simpletest.py b/examples/esp32spi/azureiot_hub_simpletest.py similarity index 100% rename from examples/ esp32spi /azureiot_hub_simpletest.py rename to examples/esp32spi/azureiot_hub_simpletest.py diff --git a/examples/ esp32spi /azureiot_hub_twin_operations.py b/examples/esp32spi/azureiot_hub_twin_operations.py similarity index 100% rename from examples/ esp32spi /azureiot_hub_twin_operations.py rename to examples/esp32spi/azureiot_hub_twin_operations.py From 9b14d25268d006306435e8b632bd938700e82383 Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Tue, 25 Jan 2022 00:08:20 -0800 Subject: [PATCH 07/11] Remove print debug & correct requirements.txt --- examples/native_networking/azureiot_hub_simpletest.py | 1 - requirements.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/native_networking/azureiot_hub_simpletest.py b/examples/native_networking/azureiot_hub_simpletest.py index 57dc741..8dad80b 100644 --- a/examples/native_networking/azureiot_hub_simpletest.py +++ b/examples/native_networking/azureiot_hub_simpletest.py @@ -71,7 +71,6 @@ pool = socketpool.SocketPool(wifi.radio) # Create an IoT Hub device client and connect device = IoTHubDevice(pool, esp, secrets["device_connection_string"]) -print(dir(device)) print("Connecting to Azure IoT Hub...") diff --git a/requirements.txt b/requirements.txt index 5e8305a..6c0c201 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,4 @@ Adafruit-Blinka Adafruit-CircuitPython-miniMQTT -Adafruit-CircuitPython-Requests Adafruit-CircuitPython-Binascii From 96454b98acb02d1e1e8215969415c9104fa57dfc Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Tue, 25 Jan 2022 16:10:47 -0800 Subject: [PATCH 08/11] Builds with Sphinx 4.3.2 --- .github/workflows/build.yml | 4 ++-- docs/requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 474520d..de16b03 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,9 +42,9 @@ jobs: # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) run: | source actions-ci/install.sh - - name: Pip install Sphinx, pre-commit + - name: Pip install Sphinx@4.3.2, pre-commit run: | - pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit + pip install --force-reinstall Sphinx@4.3.2 sphinx-rtd-theme pre-commit - name: Library version run: git describe --dirty --always --tags - name: Pre-commit hooks diff --git a/docs/requirements.txt b/docs/requirements.txt index 88e6733..32672da 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: Unlicense -sphinx>=4.0.0 +sphinx>=4.3.2 From eedefb903a5c258aa55e95a2802e0311a08da404 Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Tue, 25 Jan 2022 16:16:32 -0800 Subject: [PATCH 09/11] correct version declaration --- .github/workflows/build.yml | 4 ++-- docs/requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de16b03..654d5dd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,9 +42,9 @@ jobs: # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) run: | source actions-ci/install.sh - - name: Pip install Sphinx@4.3.2, pre-commit + - name: Pip install Sphinx 4.3.2, pre-commit run: | - pip install --force-reinstall Sphinx@4.3.2 sphinx-rtd-theme pre-commit + pip install --force-reinstall Sphinx==4.3.2 sphinx-rtd-theme pre-commit - name: Library version run: git describe --dirty --always --tags - name: Pre-commit hooks diff --git a/docs/requirements.txt b/docs/requirements.txt index 32672da..64bed0e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: Unlicense -sphinx>=4.3.2 +sphinx==4.3.2 From 0cd68ccfbcd1bcd5bc4a3f8def24834da86ad33f Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Wed, 26 Jan 2022 17:05:30 -0800 Subject: [PATCH 10/11] fixed docstring formatting, fixed Logger type for Sphinx, Sphinx version to latest --- .github/workflows/build.yml | 4 ++-- README.rst | 6 +++--- adafruit_azureiot/device_registration.py | 2 ++ adafruit_azureiot/iot_error.py | 1 + adafruit_azureiot/iot_mqtt.py | 17 +++++++++++++++-- adafruit_azureiot/iotcentral_device.py | 16 ++++++++++++++-- adafruit_azureiot/iothub_device.py | 17 +++++++++++++++-- adafruit_azureiot/keys.py | 2 ++ docs/examples.rst | 8 ++++---- docs/requirements.txt | 2 +- 10 files changed, 59 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 654d5dd..474520d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,9 +42,9 @@ jobs: # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) run: | source actions-ci/install.sh - - name: Pip install Sphinx 4.3.2, pre-commit + - name: Pip install Sphinx, pre-commit run: | - pip install --force-reinstall Sphinx==4.3.2 sphinx-rtd-theme pre-commit + pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit - name: Library version run: git describe --dirty --always --tags - name: Pre-commit hooks diff --git a/README.rst b/README.rst index ad85487..1350f23 100644 --- a/README.rst +++ b/README.rst @@ -63,8 +63,8 @@ To create an Azure IoT Hub instance or an Azure IoT Central app, you will need a - If you are not a student, head to `aka.ms/FreeAz `_ and sign up to get $200 of credit for 30 days, as well as free tiers of a load of services. You will need a credit card for validation only, your card will not be charged. -ESP32SPI Networking -=================== +ESP32 AirLift Networking +======================== To use this library, you will need to create an ESP32_SPI WifiManager, connected to WiFi. You will also need to set the current time, as this is used to generate time-based authentication keys. One way to do this is via the `Adafruit CircuitPython NTP `_ library with the following code: @@ -79,7 +79,7 @@ To use this library, you will need to create an ESP32_SPI WifiManager, connected Native Networking ================= -To use this library, with boards that have native networking support, you need to be connected to a network. You will also need to set the current time, as this is used to generate time-based authentication keys. One way to do this is by using the `Adafruit IoT Time Service `_ via the `Adafruit Requests library `_ with the following code: +To use this library, with boards that have native networking support, you need to be connected to a network. You will also need to set the current time, as this is used to generate time-based authentication keys. One way to do this is by using the `Adafruit IoT Time API `_ via the `Adafruit Requests library `_ with the following code: .. code-block:: python diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index a59a97c..948679c 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -53,6 +53,7 @@ def __init__( logger: Logger = None, ): """Creates an instance of the device registration service + :param socket: The network socket :param str id_scope: The ID scope of the device to register :param str device_id: The device ID of the device to register @@ -163,6 +164,7 @@ def register_device(self, expiry: int) -> str: """ Registers the device with the IoT Central device registration service. Returns the hostname of the IoT hub to use over MQTT + :param int expiry: The expiry time for the registration :returns: The underlying IoT Hub that this device should connect to :rtype: str diff --git a/adafruit_azureiot/iot_error.py b/adafruit_azureiot/iot_error.py index 4bc5f26..dda05fe 100644 --- a/adafruit_azureiot/iot_error.py +++ b/adafruit_azureiot/iot_error.py @@ -20,6 +20,7 @@ class IoTError(Exception): def __init__(self, message: str): """Create the IoT Error + :param str message: The error message """ super().__init__(message) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 47fb622..10ba960 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -19,6 +19,7 @@ import adafruit_minimqtt.adafruit_minimqtt as MQTT import adafruit_logging as logging +from adafruit_logging import Logger from .iot_error import IoTError from .keys import compute_derived_symmetric_key @@ -31,6 +32,7 @@ class IoTResponse: def __init__(self, code: int, message: str): """Creates an IoT Response object + :param int code: The HTTP response code for this method call, for example 200 if the method was handled successfully :param str message: The HTTP response message for this method call """ @@ -43,17 +45,20 @@ class IoTMQTTCallback: def message_sent(self, data: str) -> None: """Called when a message is sent to the cloud + :param str data: The data send with the message """ def connection_status_change(self, connected: bool) -> None: """Called when the connection status changes + :param bool connected: True if the device is connected, otherwise false """ # pylint: disable=W0613, R0201 def direct_method_invoked(self, method_name: str, payload: str) -> IoTResponse: """Called when a direct method is invoked + :param str method_name: The name of the method that was invoked :param str payload: The payload with the message :returns: A response with a code and status to show if the method was correctly handled @@ -64,6 +69,7 @@ def direct_method_invoked(self, method_name: str, payload: str) -> IoTResponse: # pylint: disable=C0103 def cloud_to_device_message_received(self, body: str, properties: dict) -> None: """Called when a cloud to device message is received + :param str body: The body of the message :param dict properties: The propreties sent with the mesage """ @@ -72,6 +78,7 @@ def device_twin_desired_updated( self, desired_property_name: str, desired_property_value, desired_version: int ) -> None: """Called when the device twin desired properties are updated + :param str desired_property_name: The name of the desired property that was updated :param desired_property_value: The value of the desired property that was updated :param int desired_version: The version of the desired property that was updated @@ -84,6 +91,7 @@ def device_twin_reported_updated( reported_version: int, ) -> None: """Called when the device twin reported values are updated + :param str reported_property_name: The name of the reported property that was updated :param reported_property_value: The value of the reported property that was updated :param int reported_version: The version of the reported property that was updated @@ -319,9 +327,10 @@ def __init__( device_id: str, device_sas_key: str, token_expires: int = 21600, - logger: logging = None, + logger: Logger = None, ): """Create the Azure IoT MQTT client + :param IoTMQTTCallback callback: A callback class :param socket: The socket to communicate over :param iface: The network interface to communicate over @@ -329,7 +338,7 @@ def __init__( :param str device_id: The device ID of the device to register :param str device_sas_key: The primary or secondary key of the device to register :param int token_expires: The number of seconds till the token expires, defaults to 6 hours - :param adafruit_logging logger: The logger + :param Logger logger: The logger """ self._callback = callback self._socket = socket @@ -372,6 +381,7 @@ def _subscribe_to_twin_topics(self): def connect(self) -> bool: """Connects to the MQTT broker + :returns: True if the connection is successful, otherwise False :rtype: bool """ @@ -426,6 +436,7 @@ def reconnect(self) -> None: def is_connected(self) -> bool: """Gets if there is an open connection to the MQTT broker + :returns: True if there is an open connection, False if not :rtype: bool """ @@ -443,6 +454,7 @@ def send_device_to_cloud_message( self, message, system_properties: dict = None ) -> None: """Send a device to cloud message from this device to Azure IoT Hub + :param message: The message data as a JSON string or a dictionary :param system_properties: System properties to send with the message :raises: ValueError if the message is not a string or dictionary @@ -472,6 +484,7 @@ def send_device_to_cloud_message( def send_twin_patch(self, patch) -> None: """Send a patch for the reported properties of the device twin + :param patch: The patch as a JSON string or a dictionary :raises: IoTError if the data is not a string or dictionary :raises RuntimeError: if the internet connection is not responding or is unable to connect diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index 96e0180..53fbf51 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -15,6 +15,7 @@ import json import time import adafruit_logging as logging +from adafruit_logging import Logger from .device_registration import DeviceRegistration from .iot_error import IoTError from .iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse @@ -25,6 +26,7 @@ class IoTCentralDevice(IoTMQTTCallback): def connection_status_change(self, connected: bool) -> None: """Called when the connection status changes + :param bool connected: True if the device is connected, otherwise false """ if self.on_connection_status_changed is not None: @@ -34,6 +36,7 @@ def connection_status_change(self, connected: bool) -> None: # pylint: disable=W0613, R0201 def direct_method_called(self, method_name: str, payload: str) -> IoTResponse: """Called when a direct method is invoked + :param str method_name: The name of the method that was invoked :param str payload: The payload with the message :returns: A response with a code and status to show if the method was correctly handled @@ -49,6 +52,7 @@ def device_twin_desired_updated( self, desired_property_name: str, desired_property_value, desired_version: int ) -> None: """Called when the device twin desired properties are updated + :param str desired_property_name: The name of the desired property that was updated :param desired_property_value: The value of the desired property that was updated :param int desired_version: The version of the desired property that was updated @@ -69,6 +73,7 @@ def device_twin_reported_updated( reported_version: int, ) -> None: """Called when the device twin reported values are updated + :param str reported_property_name: The name of the reported property that was updated :param reported_property_value: The value of the reported property that was updated :param int reported_version: The version of the reported property that was updated @@ -88,16 +93,17 @@ def __init__( device_id: str, device_sas_key: str, token_expires: int = 21600, - logger: logging = None, + logger: Logger = None, ): """Create the Azure IoT Central device client + :param socket: The network socket :param iface: The network interface :param str id_scope: The ID Scope of the device in IoT Central :param str device_id: The device ID of the device in IoT Central :param str device_sas_key: The primary or secondary key of the device in IoT Central :param int token_expires: The number of seconds till the token expires, defaults to 6 hours - :param adafruit_logging logger: The logger + :param Logger logger: The logger """ self._socket = socket self._iface = iface @@ -132,6 +138,7 @@ def property_changed(_property_name: str, property_value, version: int) -> None def connect(self) -> None: """Connects to Azure IoT Central + :raises DeviceRegistrationError: if the device cannot be registered successfully :raises RuntimeError: if the internet connection is not responding or is unable to connect """ @@ -166,6 +173,7 @@ def connect(self) -> None: def disconnect(self) -> None: """Disconnects from the MQTT broker + :raises IoTError: if there is no open connection to the MQTT broker """ if self._mqtt is None: @@ -182,6 +190,7 @@ def reconnect(self) -> None: def is_connected(self) -> bool: """Gets if there is an open connection to the MQTT broker + :returns: True if there is an open connection, False if not :rtype: bool """ @@ -189,6 +198,7 @@ def is_connected(self) -> bool: def loop(self) -> None: """Listens for MQTT messages + :raises IoTError: if there is no open connection to the MQTT broker """ if self._mqtt is None: @@ -198,6 +208,7 @@ def loop(self) -> None: def send_property(self, property_name: str, value) -> None: """Updates the value of a writable property + :param str property_name: The name of the property to write to :param value: The value to set on the property :raises IoTError: if there is no open connection to the MQTT broker @@ -211,6 +222,7 @@ def send_property(self, property_name: str, value) -> None: def send_telemetry(self, data) -> None: """Sends telemetry to the IoT Central app + :param data: The telemetry data to send :raises IoTError: if there is no open connection to the MQTT broker """ diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index 06288e2..e4f0410 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -19,6 +19,7 @@ import json import adafruit_logging as logging +from adafruit_logging import Logger from .iot_error import IoTError from .iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse @@ -65,6 +66,7 @@ class IoTHubDevice(IoTMQTTCallback): def connection_status_change(self, connected: bool) -> None: """Called when the connection status changes + :param bool connected: True if the device is connected, otherwise false """ if self._on_connection_status_changed is not None: @@ -74,6 +76,7 @@ def connection_status_change(self, connected: bool) -> None: # pylint: disable=W0613, R0201 def direct_method_invoked(self, method_name: str, payload: str) -> IoTResponse: """Called when a direct method is invoked + :param str method_name: The name of the method that was invoked :param str payload: The payload with the message :returns: A response with a code and status to show if the method was correctly handled @@ -88,6 +91,7 @@ def direct_method_invoked(self, method_name: str, payload: str) -> IoTResponse: # pylint: disable=C0103 def cloud_to_device_message_received(self, body: str, properties: dict) -> None: """Called when a cloud to device message is received + :param str body: The body of the message :param dict properties: The propreties sent with the mesage """ @@ -102,6 +106,7 @@ def device_twin_desired_updated( desired_version: int, ) -> None: """Called when the device twin desired properties are updated + :param str desired_property_name: The name of the desired property that was updated :param desired_property_value: The value of the desired property that was updated :param int desired_version: The version of the desired property that was updated @@ -119,6 +124,7 @@ def device_twin_reported_updated( reported_version: int, ) -> None: """Called when the device twin reported values are updated + :param str reported_property_name: The name of the reported property that was updated :param reported_property_value: The value of the reported property that was updated :param int reported_version: The version of the reported property that was updated @@ -135,14 +141,15 @@ def __init__( iface, device_connection_string: str, token_expires: int = 21600, - logger: logging = None, + logger: Logger = None, ): """Create the Azure IoT Central device client + :param socket: The network socket :param iface: The network interface :param str device_connection_string: The Iot Hub device connection string :param int token_expires: The number of seconds till the token expires, defaults to 6 hours - :param adafruit_logging logger: The logger + :param Logger logger: The logger """ self._socket = socket self._iface = iface @@ -286,6 +293,7 @@ def device_twin_reported_updated(reported_property_name: str, reported_property_ def connect(self) -> None: """Connects to Azure IoT Hub + :raises RuntimeError: if the internet connection is not responding or is unable to connect """ self._mqtt = IoTMQTT( @@ -308,6 +316,7 @@ def connect(self) -> None: def loop(self) -> None: """Listens for MQTT messages + :raises IoTError: if there is no open connection to the MQTT broker """ if self._mqtt is None: @@ -317,6 +326,7 @@ def loop(self) -> None: def disconnect(self) -> None: """Disconnects from the MQTT broker + :raises IoTError: if there is no open connection to the MQTT broker """ if self._mqtt is None: @@ -333,6 +343,7 @@ def reconnect(self) -> None: def is_connected(self) -> bool: """Gets if there is an open connection to the MQTT broker + :returns: True if there is an open connection, False if not :rtype: bool """ @@ -342,6 +353,7 @@ def send_device_to_cloud_message( self, message: Union[str, dict], system_properties: dict = None ) -> None: """Send a device to cloud message from this device to Azure IoT Hub + :param message: The message data as a JSON string or a dictionary :param system_properties: System properties to send with the message :raises: ValueError if the message is not a string or dictionary @@ -354,6 +366,7 @@ def send_device_to_cloud_message( def update_twin(self, patch: Union[str, dict]) -> None: """Updates the reported properties in the devices device twin + :param patch: The JSON patch to apply to the device twin reported properties """ if self._mqtt is None: diff --git a/adafruit_azureiot/keys.py b/adafruit_azureiot/keys.py index 83d7250..107accb 100644 --- a/adafruit_azureiot/keys.py +++ b/adafruit_azureiot/keys.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT """Computes a derived symmetric key from a secret and a message + :param str secret: The secret to use for the key :param str msg: The message to use for the key :returns: The derived symmetric key @@ -15,6 +16,7 @@ def compute_derived_symmetric_key(secret: str, msg: str) -> bytes: """Computes a derived symmetric key from a secret and a message + :param str secret: The secret to use for the key :param str msg: The message to use for the key :returns: The derived symmetric key diff --git a/docs/examples.rst b/docs/examples.rst index 2790cfd..6ea5ea0 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,5 +1,5 @@ -IoT Hub ESP32SPI Networking ---------------------------- +IoT Hub ESP32 AirLift Networking +-------------------------------- Ensure your IoT Hub device works with this simple test. @@ -25,8 +25,8 @@ Update the reported properties of the devices device twin, and receive updates t :caption: examples/esp32spi/azureiot_hub_twin_operations.py :linenos: -IoT Central ESP32SPI Networking -------------------------------- +IoT Central ESP32 AirLift Networking +------------------------------------ Ensure your IoT Central device works with this simple test. diff --git a/docs/requirements.txt b/docs/requirements.txt index 64bed0e..dcea916 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: Unlicense -sphinx==4.3.2 +sphinx From 79c50ef1adec54b90525279d02df3b1225ee74df Mon Sep 17 00:00:00 2001 From: Patrick <4002194+askpatrickw@users.noreply.github.com> Date: Wed, 26 Jan 2022 18:03:46 -0800 Subject: [PATCH 11/11] correct reference to ESP32 Airlift --- adafruit_azureiot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_azureiot/__init__.py b/adafruit_azureiot/__init__.py index 5cca8fd..1bfb16e 100644 --- a/adafruit_azureiot/__init__.py +++ b/adafruit_azureiot/__init__.py @@ -19,7 +19,7 @@ * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases -**With ESP32SPI Peripheral Networking** +**With ESP32 Airlift Networking** * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice * Adafruit's ESP32SPI library: https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI