From c384d4210f5db1b8ac0e85cc5be9c48b28a66a5c Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sat, 11 Apr 2020 14:27:32 -0700 Subject: [PATCH 01/33] Copying in code from the external repo --- .gitignore | 3 +- .pylintrc | 4 +- adafruit_azureiot.py | 240 ----------- adafruit_azureiot/__init__.py | 46 ++ adafruit_azureiot/constants.py | 6 + adafruit_azureiot/device_registration.py | 225 ++++++++++ adafruit_azureiot/iot_error.py | 13 + adafruit_azureiot/iot_mqtt.py | 403 ++++++++++++++++++ adafruit_azureiot/iotcentral_device.py | 123 ++++++ adafruit_azureiot/iothub_device.py | 171 ++++++++ docs/conf.py | 2 +- examples/azureiot_device_management.py | 44 -- examples/azureiot_devicetwin.py | 53 --- examples/iotcentral_commands.py | 0 examples/iotcentral_properties.py | 0 ...simpletest.py => iotcentral_simpletest.py} | 0 examples/iotcentral_telemetry.py | 0 examples/iothub_directmethods.py | 0 examples/iothub_messages.py | 0 examples/iothub_simpletest.py | 0 examples/iothub_twin_operations.py | 0 requirements.txt | 6 +- setup.py | 4 +- 23 files changed, 999 insertions(+), 344 deletions(-) delete mode 100644 adafruit_azureiot.py create mode 100644 adafruit_azureiot/__init__.py create mode 100644 adafruit_azureiot/constants.py create mode 100644 adafruit_azureiot/device_registration.py create mode 100644 adafruit_azureiot/iot_error.py create mode 100644 adafruit_azureiot/iot_mqtt.py create mode 100644 adafruit_azureiot/iotcentral_device.py create mode 100755 adafruit_azureiot/iothub_device.py delete mode 100644 examples/azureiot_device_management.py delete mode 100644 examples/azureiot_devicetwin.py create mode 100644 examples/iotcentral_commands.py create mode 100644 examples/iotcentral_properties.py rename examples/{azureiot_simpletest.py => iotcentral_simpletest.py} (100%) create mode 100644 examples/iotcentral_telemetry.py create mode 100644 examples/iothub_directmethods.py create mode 100644 examples/iothub_messages.py create mode 100644 examples/iothub_simpletest.py create mode 100644 examples/iothub_twin_operations.py diff --git a/.gitignore b/.gitignore index c83f8b7..df3a832 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ bundles *.DS_Store .eggs dist -**/*.egg-info \ No newline at end of file +**/*.egg-info +.vscode/settings.json diff --git a/.pylintrc b/.pylintrc index d8f0ee8..2695033 100644 --- a/.pylintrc +++ b/.pylintrc @@ -217,7 +217,7 @@ indent-after-paren=4 indent-string=' ' # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=140 # Maximum number of lines in a module max-module-lines=1000 @@ -395,7 +395,7 @@ valid-metaclass-classmethod-first-arg=mcs [DESIGN] # Maximum number of arguments for function / method -max-args=5 +max-args=6 # Maximum number of attributes for a class (see R0902). # max-attributes=7 diff --git a/adafruit_azureiot.py b/adafruit_azureiot.py deleted file mode 100644 index f78d5d6..0000000 --- a/adafruit_azureiot.py +++ /dev/null @@ -1,240 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2019 Brent Rubell for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -""" -`adafruit_azureiot` -================================================================================ - -Microsoft Azure IoT for CircuitPython - -* Author(s): Brent Rubell - -Implementation Notes --------------------- - -**Software and Dependencies:** - -* Adafruit CircuitPython firmware for the supported boards: - https://github.com/adafruit/circuitpython/releases - -* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice -* Adafruit's ESP32SPI library: https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI -""" - -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AzureIoT.git" - -AZ_API_VER = "2018-06-30" # Azure URI API Version Identifier -AZURE_HTTP_ERROR_CODES = [400, 401, 404, 403, 412, 429, 500] # Azure HTTP Status Codes - - -class IOT_Hub: - """ - Provides access to a Microsoft Azure IoT Hub. - https://docs.microsoft.com/en-us/rest/api/iothub/ - """ - - def __init__(self, wifi_manager, iot_hub_name, sas_token, device_id): - """ Creates an instance of an Azure IoT Hub Client. - :param wifi_manager: WiFiManager object from ESPSPI_WiFiManager. - :param str iot_hub_name: Name of your IoT Hub. - :param str sas_token: Azure IoT Hub SAS Token Identifier. - :param str device_id: Unique Azure IoT Device Identifier. - """ - _wifi_type = str(type(wifi_manager)) - if "ESPSPI_WiFiManager" in _wifi_type: - self._wifi = wifi_manager - else: - raise TypeError("This library requires a WiFiManager object.") - self._iot_hub_url = "https://{0}.azure-devices.net".format(iot_hub_name) - self._sas_token = sas_token - self._device_id = device_id - self._azure_header = {"Authorization": self._sas_token} - - @property - def device_id(self): - """Returns the current device identifier""" - return self._device_id - - @device_id.setter - def device_id(self, device_identifier): - """Sets the current device identifier - :param str device_identifier: Unique device identifier. - """ - self._device_id = device_identifier - - @staticmethod - def _parse_http_status(status_code, status_reason): - """Parses status code, throws error based on Azure IoT Common Error Codes. - :param int status_code: HTTP status code. - :param str status_reason: Description of HTTP status. - """ - for error in AZURE_HTTP_ERROR_CODES: - if error == status_code: - raise TypeError("Error {0}: {1}".format(status_code, status_reason)) - - # Cloud-to-Device Messaging - def get_hub_message(self): - """Returns a message from a Microsoft Azure IoT Hub (Cloud-to-Device). - Returns None if the message queue is empty. - NOTE: HTTP Cloud-to-Device messages are throttled. Poll every 25+ minutes. - """ - reject_message = True - # get a device-bound notification - path = "{0}/devices/{1}/messages/deviceBound?api-version={2}".format( - self._iot_hub_url, self._device_id, AZ_API_VER - ) - data = self._get(path, is_c2d=True) - if data == 204: # device's message queue is empty - return None - etag = data[1]["etag"] - if etag: # either complete or nack the message - reject_message = False - path_complete = "{0}/devices/{1}/messages/deviceBound/{2}?api-version={3}".format( - self._iot_hub_url, self._device_id, etag.strip("'\""), AZ_API_VER - ) - if reject_message: - path_complete += "&reject" - del_status = self._delete(path_complete) - if del_status == 204: - return data[0] - return None - - # Device-to-Cloud Messaging - def send_device_message(self, message): - """Sends a device-to-cloud message. - :param string message: Message to send to Azure IoT. - """ - path = "{0}/devices/{1}/messages/events?api-version={2}".format( - self._iot_hub_url, self._device_id, AZ_API_VER - ) - self._post(path, message, return_response=False) - - # Device Twin - def get_device_twin(self): - """Returns the device's device twin information in JSON format. - """ - path = "{0}/twins/{1}?api-version={2}".format( - self._iot_hub_url, self._device_id, AZ_API_VER - ) - return self._get(path) - - def update_device_twin(self, properties): - """Updates tags and desired properties of the device's device twin. - :param str properties: Device Twin Properties - (https://docs.microsoft.com/en-us/rest/api/iothub/service/updatetwin#twinproperties) - """ - path = "{0}/twins/{1}?api-version={2}".format( - self._iot_hub_url, self._device_id, AZ_API_VER - ) - return self._patch(path, properties) - - def replace_device_twin(self, properties): - """Replaces tags and desired properties of a device twin. - :param str properties: Device Twin Properties. - """ - path = "{0}/twins/{1}?api-version-{2}".format( - self._iot_hub_url, self._device_id, AZ_API_VER - ) - return self._put(path, properties) - - # IoT Hub Service - def get_devices(self): - """Enumerate devices from the identity registry of the IoT Hub. - """ - path = "{0}/devices/?api-version={1}".format(self._iot_hub_url, AZ_API_VER) - return self._get(path) - - def get_device(self): - """Gets device information from the identity - registry of an IoT Hub. - """ - path = "{0}/devices/{1}?api-version={2}".format( - self._iot_hub_url, self._device_id, AZ_API_VER - ) - return self._get(path) - - # HTTP Helper Methods - def _post(self, path, payload, return_response=True): - """HTTP POST - :param str path: Formatted Azure IOT Hub Path. - :param str payload: JSON-formatted Data Payload. - """ - response = self._wifi.post(path, json=payload, headers=self._azure_header) - self._parse_http_status(response.status_code, response.reason) - if return_response: - return response.json() - return response.text - - def _get(self, path, is_c2d=False): - """HTTP GET - :param str path: Formatted Azure IOT Hub Path. - :param bool is_c2d: Cloud-to-device get request. - """ - response = self._wifi.get(path, headers=self._azure_header) - status_code = response.status_code - if is_c2d: - if status_code == 200: - data = response.text - headers = response.headers - response.close() - return data, headers - response.close() - return status_code - json = response.json() - response.close() - return json - - def _delete(self, path, etag=None): - """HTTP DELETE - :param str path: Formatted Azure IOT Hub Path. - """ - if etag: - data_headers = {"Authorization": self._sas_token, "If-Match": '"%s"' % etag} - else: - data_headers = self._azure_header - response = self._wifi.delete(path, headers=data_headers) - self._parse_http_status(response.status_code, response.reason) - status_code = response.status_code - response.close() - return status_code - - def _patch(self, path, payload): - """HTTP PATCH - :param str path: Formatted Azure IOT Hub Path. - :param str payload: JSON-formatted payload. - """ - response = self._wifi.patch(path, json=payload, headers=self._azure_header) - self._parse_http_status(response.status_code, response.reason) - json_data = response.json() - response.close() - return json_data - - def _put(self, path, payload=None): - """HTTP PUT - :param str path: Formatted Azure IOT Hub Path. - :param str payload: JSON-formatted payload. - """ - response = self._wifi.put(path, json=payload, headers=self._azure_header) - self._parse_http_status(response.status_code, response.reason) - json_data = response.json() - response.close() - return json_data diff --git a/adafruit_azureiot/__init__.py b/adafruit_azureiot/__init__.py new file mode 100644 index 0000000..131aeb1 --- /dev/null +++ b/adafruit_azureiot/__init__.py @@ -0,0 +1,46 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Jim Bennett +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_azureiot` +================================================================================ + +Microsoft Azure IoT for CircuitPython + +* Author(s): Jim Bennett, Elena Horton + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +* Adafruit's ESP32SPI library: https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI +* Community HMAC library: https://github.com/jimbobbennett/CircuitPython_HMAC +* Community base64 library: https://github.com/jimbobbennett/CircuitPython_Base64 +* Community Parse library: https://github.com/jimbobbennett/CircuitPython_Parse +""" + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AzureIoT.git" diff --git a/adafruit_azureiot/constants.py b/adafruit_azureiot/constants.py new file mode 100644 index 0000000..cc7330b --- /dev/null +++ b/adafruit_azureiot/constants.py @@ -0,0 +1,6 @@ +"""This file is for maintaining constants that could be changed or added to over time for different scenarios +""" + +DPS_API_VERSION = "2018-11-01" +IOTC_API_VERSION = "2016-11-14" +DPS_END_POINT = "global.azure-devices-provisioning.net" diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py new file mode 100644 index 0000000..2b3a560 --- /dev/null +++ b/adafruit_azureiot/device_registration.py @@ -0,0 +1,225 @@ +""" +Device Registration +===================== + +Handles registration of IoT Central devices, and gets the hostname to use when connecting +to IoT Central over MQTT +""" + +import gc +import json +import time +import circuitpython_base64 as base64 +import circuitpython_hmac as hmac +import circuitpython_parse as parse +from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager +import adafruit_logging as logging +from adafruit_logging import Logger +import adafruit_hashlib as hashlib +from . import constants + + +AZURE_HTTP_ERROR_CODES = [400, 401, 404, 403, 412, 429, 500] # Azure HTTP Status Codes + + +class DeviceRegistrationError(Exception): + """ + An error from the device registration + """ + + def __init__(self, message): + super(DeviceRegistrationError, self).__init__(message) + self.message = message + + +class DeviceRegistration: + """ + Handles registration of IoT Central devices, and gets the hostname to use when connecting + to IoT Central over MQTT + """ + + _dps_endpoint = constants.DPS_END_POINT + _dps_api_version = constants.DPS_API_VERSION + _loop_interval = 2 + + @staticmethod + def _parse_http_status(status_code, status_reason): + """Parses status code, throws error based on Azure IoT Common Error Codes. + :param int status_code: HTTP status code. + :param str status_reason: Description of HTTP status. + """ + for error in AZURE_HTTP_ERROR_CODES: + if error == status_code: + raise TypeError("Error {0}: {1}".format(status_code, status_reason)) + + def __init__(self, wifi_manager: ESPSPI_WiFiManager, id_scope: str, device_id: str, key: str, logger: Logger = None): + """Creates an instance of the device registration + :param wifi_manager: WiFiManager object from ESPSPI_WiFiManager. + :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 adafruit_logging.Logger key: The primary or secondary key of the device to register + """ + wifi_type = str(type(wifi_manager)) + if "ESPSPI_WiFiManager" not in wifi_type: + raise TypeError("This library requires a WiFiManager object.") + + self._wifi_manager = wifi_manager + self._id_scope = id_scope + self._device_id = device_id + self._key = key + self._logger = logger if logger is not None else logging.getLogger("log") + + @staticmethod + def compute_derived_symmetric_key(secret, reg_id): + """Computes a derived symmetric key from a secret and a message + """ + secret = base64.b64decode(secret) + return base64.b64encode(hmac.new(secret, msg=reg_id.encode("utf8"), digestmod=hashlib.sha256).digest()) + + def _loop_assign(self, operation_id, headers) -> str: + uri = "https://%s/%s/registrations/%s/operations/%s?api-version=%s" % ( + self._dps_endpoint, + self._id_scope, + self._device_id, + operation_id, + self._dps_api_version, + ) + self._logger.info("- iotc :: _loop_assign :: " + uri) + target = parse.urlparse(uri) + + response = self.__run_get_request_with_retry(target.geturl(), headers) + + try: + data = response.json() + except Exception as error: + err = "ERROR: " + str(error) + " => " + str(response) + self._logger.error(err) + raise DeviceRegistrationError(err) + + loop_try = 0 + + if data is not None and "status" in data: + if data["status"] == "assigning": + time.sleep(self._loop_interval) + if loop_try < 20: + loop_try = loop_try + 1 + return self._loop_assign(operation_id, headers) + + err = "ERROR: Unable to provision the device." + self._logger.error(err) + raise DeviceRegistrationError(err) + + if data["status"] == "assigned": + state = data["registrationState"] + return state["assignedHub"] + else: + data = str(data) + + err = "DPS L => " + str(data) + self._logger.error(err) + raise DeviceRegistrationError(err) + + def __run_put_request_with_retry(self, url, body, headers): + retry = 0 + response = None + + while True: + gc.collect() + try: + self._logger.debug("Trying to send...") + response = self._wifi_manager.put(url, json=body, headers=headers) + self._logger.debug("Sent!") + break + except RuntimeError as runtime_error: + self._logger.info("Could not send data, retrying after 0.5 seconds: " + str(runtime_error)) + retry = retry + 1 + + if retry >= 10: + self._logger.error("Failed to send data") + raise + + time.sleep(0.5) + continue + + gc.collect() + return response + + def __run_get_request_with_retry(self, url, headers): + retry = 0 + response = None + + while True: + gc.collect() + try: + self._logger.debug("Trying to send...") + response = self._wifi_manager.get(url, headers=headers) + self._logger.debug("Sent!") + break + except RuntimeError as runtime_error: + self._logger.info("Could not send data, retrying after 0.5 seconds: " + str(runtime_error)) + retry = retry + 1 + + if retry >= 10: + self._logger.error("Failed to send data") + raise + + time.sleep(0.5) + continue + + gc.collect() + return response + + 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 str expiry: The expiry time + """ + # pylint: disable=c0103 + sr = self._id_scope + "%2Fregistrations%2F" + self._device_id + sig_no_encode = DeviceRegistration.compute_derived_symmetric_key(self._key, sr + "\n" + str(expiry)) + sig_encoded = parse.quote(sig_no_encode, "~()*!.'") + auth_string = "SharedAccessSignature sr=" + sr + "&sig=" + sig_encoded + "&se=" + str(expiry) + "&skn=registration" + + headers = { + "content-type": "application/json; charset=utf-8", + "user-agent": "iot-central-client/1.0", + "Accept": "*/*", + } + + if auth_string is not None: + headers["authorization"] = auth_string + + body = {"registrationId": self._device_id} + + uri = "https://%s/%s/registrations/%s/register?api-version=%s" % ( + self._dps_endpoint, + self._id_scope, + self._device_id, + self._dps_api_version, + ) + target = parse.urlparse(uri) + + self._logger.info("Connecting...") + self._logger.info("URL: " + target.geturl()) + self._logger.info("body: " + json.dumps(body)) + print("headers: " + json.dumps(headers)) + + response = self.__run_put_request_with_retry(target.geturl(), body, headers) + + data = None + try: + data = response.json() + except Exception as e: + err = "ERROR: non JSON is received from " + self._dps_endpoint + " => " + str(response) + " .. message : " + str(e) + self._logger.error(err) + raise DeviceRegistrationError(err) + + if "errorCode" in data: + err = "DPS => " + str(data) + self._logger.error(err) + raise DeviceRegistrationError(err) + + time.sleep(1) + return self._loop_assign(data["operationId"], headers) diff --git a/adafruit_azureiot/iot_error.py b/adafruit_azureiot/iot_error.py new file mode 100644 index 0000000..3b1cd9d --- /dev/null +++ b/adafruit_azureiot/iot_error.py @@ -0,0 +1,13 @@ +""" +An error from the IoT service +""" + + +class IoTError(Exception): + """ + An error from the IoT service + """ + + def __init__(self, message): + super(IoTError, self).__init__(message) + self.message = message diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py new file mode 100644 index 0000000..0223d46 --- /dev/null +++ b/adafruit_azureiot/iot_mqtt.py @@ -0,0 +1,403 @@ +"""MQTT client for Azure IoT +""" + +import gc +import json +import time +from adafruit_minimqtt import MQTT +import circuitpython_parse as parse +from device_registration import DeviceRegistration +import adafruit_logging as logging +from . import constants + + +class IoTResponse: + """A response from a direct method call + """ + + def __init__(self, code, message): + self._code = code + self._message = message + + def get_response_code(self): + """Gets the method response code + """ + return self._code + + def get_response_message(self): + """Gets the method response message + """ + return self._message + + +class IoTMQTTCallback: + """An interface for classes that can be called by MQTT events + """ + + def message_sent(self, data) -> None: + """Called when a message is sent to the cloud + """ + + def connection_status_change(self, connected: bool) -> None: + """Called when the connection status changes + """ + + # pylint: disable=W0613, R0201 + def direct_method_called(self, method_name: str, data) -> IoTResponse: + """Called when a direct method is invoked + """ + return 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 + """ + + 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 + """ + + def device_twin_reported_updated(self, reported_property_name: str, reported_property_value, reported_version: int) -> None: + """Called when the device twin reported values are updated + """ + + def settings_updated(self) -> None: + """Called when the settings are updated + """ + + +# pylint: disable=R0902 +class IoTMQTT: + """MQTT client for Azure IoT + """ + + _iotc_api_version = constants.IOTC_API_VERSION + + def _gen_sas_token(self): + token_expiry = int(time.time() + self._token_expires) + uri = self._hostname + "%2Fdevices%2F" + self._device_id + signed_hmac_sha256 = DeviceRegistration.compute_derived_symmetric_key(self._key, uri + "\n" + str(token_expiry)) + signature = parse.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(uri, signature, token_expiry) + return token + + # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/25 + def _try_create_mqtt_client(self, hostname): + self._mqtts = MQTT( + broker=hostname, + username=self._username, + password=self._passwd, + port=8883, + keep_alive=120, + is_ssl=True, + client_id=self._device_id, + log=True, + ) + + self._mqtts.logger.setLevel(logging.INFO) + + # set actions to take throughout connection lifecycle + self._mqtts.on_connect = self._on_connect + self._mqtts.on_message = self._on_message + self._mqtts.on_log = self._on_log + self._mqtts.on_publish = self._on_publish + self._mqtts.on_disconnect = self._on_disconnect + + # initiate the connection using the adafruit_minimqtt library + self._mqtts.last_will() + self._mqtts.connect() + + def _create_mqtt_client(self): + try: + self._try_create_mqtt_client(self._hostname) + except ValueError: + # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/25 + self._try_create_mqtt_client("https://" + self._hostname) + + # pylint: disable=C0103, W0613 + def _on_connect(self, client, userdata, _, rc): + self._logger.info("- iot_mqtt :: _on_connect :: rc = " + str(rc) + ", userdata = " + str(userdata)) + if rc == 0: + self._mqtt_connected = True + self._auth_response_received = True + self._callback.connection_status_change(True) + + # pylint: disable=C0103, W0613 + def _on_log(self, client, userdata, level, buf): + self._logger.info("mqtt-log : " + buf) + if level <= 8: + self._logger.error("mqtt-log : " + buf) + + def _on_disconnect(self, client, userdata, rc): + self._logger.info("- iot_mqtt :: _on_disconnect :: rc = " + str(rc)) + self._auth_response_received = True + + if rc == 5: + self._logger.error("on(disconnect) : Not authorized") + self.disconnect() + + if rc == 1: + self._mqtt_connected = False + + if rc != 5: + self._callback.connection_status_change(False) + + def _on_publish(self, client, data, topic, msg_id): + self._logger.info("- iot_mqtt :: _on_publish :: " + str(data) + " on topic " + str(topic)) + + # pylint: disable=W0703 + def _handle_device_twin_update(self, msg: str, topic: str): + self._logger.debug("- iot_mqtt :: _echo_desired :: " + topic) + twin = None + desired = None + + print(msg) + + try: + twin = json.loads(msg) + except Exception as e: + self._logger.error("ERROR: JSON parse for Device Twin message object has failed. => " + msg + " => " + str(e)) + return + + if "reported" in twin: + reported = twin["reported"] + + if "$version" in reported: + reported_version = reported["$version"] + reported.pop("$version") + else: + self._logger.error("ERROR: Unexpected payload for reported twin update => " + msg) + return + + for property_name, value in reported.items(): + self._callback.device_twin_reported_updated(property_name, value, reported_version) + + is_patch = "desired" not in twin + + if is_patch: + desired = twin + else: + desired = twin["desired"] + + if "$version" in desired: + desired_version = desired["$version"] + desired.pop("$version") + else: + self._logger.error("ERROR: Unexpected payload for desired twin update => " + msg) + return + + for property_name, value in desired.items(): + self._callback.device_twin_desired_updated(property_name, value, desired_version) + + def _handle_direct_method(self, msg: str, topic: str): + index = topic.find("$rid=") + method_id = 1 + method_name = "None" + if index == -1: + self._logger.error("ERROR: C2D doesn't include topic id") + else: + method_id = topic[index + 5 :] + topic_template = "$iothub/methods/POST/" + len_temp = len(topic_template) + method_name = topic[len_temp : topic.find("/", len_temp + 1)] + + ret = self._callback.direct_method_called(method_name, msg) + + ret_code = 200 + ret_message = "{}" + if ret.get_response_code() is not None: + ret_code = ret.get_response_code() + if ret.get_response_message() is not None: + ret_message = ret.get_response_message() + + # ret message must be JSON + if not ret_message.startswith("{") or not ret_message.endswith("}"): + ret_json = {"Value": ret_message} + ret_message = json.dumps(ret_json) + + next_topic = "$iothub/methods/res/{}/?$rid={}".format(ret_code, method_id) + self._logger.info("C2D: => " + next_topic + " with data " + ret_message + " and name => " + method_name) + self._send_common(next_topic, ret_message) + + def _handle_cloud_to_device_message(self, msg: str, topic: str): + parts = topic.split("&")[1:] + + properties = {} + for part in parts: + key_value = part.split("=") + properties[key_value[0]] = key_value[1] + + self._callback.cloud_to_device_message_received(msg, properties) + + # pylint: disable=W0702, R0912 + def _on_message(self, client, msg_topic, payload): + topic = "" + msg = None + + print("Topic: ", str(msg_topic)) + self._logger.info("- iot_mqtt :: _on_message :: payload(" + str(payload) + ")") + + if payload is not None: + try: + msg = payload.decode("utf-8") + except: + msg = str(payload) + + if msg_topic is not None: + try: + topic = msg_topic.decode("utf-8") + except: + topic = str(msg_topic) + + if topic.startswith("$iothub/"): + if topic.startswith("$iothub/twin/PATCH/properties/desired/") or topic.startswith("$iothub/twin/res/200/?$rid="): + self._handle_device_twin_update(str(msg), topic) + elif topic.startswith("$iothub/methods"): + self._handle_direct_method(str(msg), topic) + else: + if not topic.startswith("$iothub/twin/res/"): # not twin response + self._logger.error("ERROR: unknown twin! - {}".format(msg)) + elif topic.startswith("devices/{}/messages/devicebound".format(self._device_id)): + self._handle_cloud_to_device_message(str(msg), topic) + else: + self._logger.error("ERROR: (unknown message) - {}".format(msg)) + + def _send_common(self, topic, data) -> None: + self._logger.debug("Sending message on topic: " + topic) + self._logger.debug("Sending message: " + str(data)) + + retry = 0 + + while True: + gc.collect() + try: + self._logger.debug("Trying to send...") + self._mqtts.publish(topic, data) + self._logger.debug("Data sent") + break + except RuntimeError as runtime_error: + self._logger.info("Could not send data, retrying after 0.5 seconds: " + str(runtime_error)) + retry = retry + 1 + + if retry >= 10: + self._logger.error("Failed to send data") + raise + + time.sleep(0.5) + continue + + print("finished _send_common") + gc.collect() + + def _get_device_settings(self) -> None: + self._logger.info("- iot_mqtt :: _get_device_settings :: ") + self.loop() + self._send_common("$iothub/twin/GET/?$rid=0", " ") + + # pylint: disable=R0913 + def __init__( + self, callback: IoTMQTTCallback, hostname: str, device_id: str, key: str, token_expires: int = 21600, logger: logging = None + ): + """Create the Azure IoT MQTT client + :param IoTMQTTCallback callback: A callback class + :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 int token_expires: The number of seconds till the token expires, defaults to 6 hours + :param adafruit_logging logger: The logger + """ + self._callback = callback + self._mqtt_connected = False + self._auth_response_received = False + self._mqtts = None + self._device_id = device_id + self._hostname = hostname + self._key = key + self._token_expires = token_expires + self._username = "{}/{}/api-version={}".format(self._hostname, device_id, self._iotc_api_version) + self._passwd = self._gen_sas_token() + self._logger = logger if logger is not None else logging.getLogger("log") + + def connect(self): + """Connects to the MQTT broker + """ + self._logger.info("- iot_mqtt :: connect :: " + self._hostname) + + self._create_mqtt_client() + + self._logger.info(" - iot_mqtt :: connect :: created mqtt client. connecting..") + while self._auth_response_received is None: + self.loop() + + self._logger.info(" - iot_mqtt :: connect :: on_connect must be fired. Connected ? " + str(self.is_connected())) + if not self.is_connected(): + return 1 + + self._mqtt_connected = True + self._auth_response_received = True + + self._mqtts.subscribe("devices/{}/messages/events/#".format(self._device_id)) + self._mqtts.subscribe("devices/{}/messages/devicebound/#".format(self._device_id)) + self._mqtts.subscribe("$iothub/twin/PATCH/properties/desired/#") # twin desired property changes + self._mqtts.subscribe("$iothub/twin/res/#") # twin properties response + self._mqtts.subscribe("$iothub/methods/#") + + if self._get_device_settings() == 0: + self._callback.settings_updated() + else: + return 1 + + return 0 + + def disconnect(self): + """Disconnects from the MQTT broker + """ + if not self.is_connected(): + return + + self._logger.info("- iot_mqtt :: disconnect :: ") + self._mqtt_connected = False + self._mqtts.disconnect() + + def is_connected(self): + """Gets if there is an open connection to the MQTT broker + """ + return self._mqtt_connected + + def loop(self): + """Listens for MQTT messages + """ + if not self.is_connected(): + return + + self._mqtts.loop() + + def _send_common(self, topic, data): + self._mqtts.publish(topic, data) + + def send_device_to_cloud_message(self, data, system_properties=None) -> None: + """Send a device to cloud message from this device to Azure IoT Hub + """ + self._logger.info("- iot_mqtt :: send_device_to_cloud_message :: " + data) + topic = "devices/{}/messages/events/".format(self._device_id) + + if system_properties is not None: + firstProp = True + for prop in system_properties: + if not firstProp: + topic += "&" + else: + firstProp = False + topic += prop + "=" + str(system_properties[prop]) + + self._send_common(topic, data) + self._callback.message_sent(data) + + def send_twin_patch(self, data): + """Send a patch for the reported properties of the device twin + """ + self._logger.info("- iot_mqtt :: sendProperty :: " + data) + topic = "$iothub/twin/PATCH/properties/reported/?$rid={}".format(int(time.time())) + return self._send_common(topic, data) diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py new file mode 100644 index 0000000..29adf54 --- /dev/null +++ b/adafruit_azureiot/iotcentral_device.py @@ -0,0 +1,123 @@ +"""Connectivity to Azure IoT Central +""" + +import json +import time +from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager +from device_registration import DeviceRegistration +from iot_error import IoTError +from iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse +import adafruit_logging as logging + + +class IoTCentralDevice(IoTMQTTCallback): + """A device client for the Azure IoT Central service + """ + + def connection_status_change(self, connected: bool) -> None: + """Called when the connection status changes + """ + if self.on_connection_status_changed is not None: + # pylint: disable=E1102 + self.on_connection_status_changed(connected) + + # pylint: disable=W0613, R0201 + def direct_method_called(self, method_name: str, data) -> IoTResponse: + """Called when a direct method is invoked + """ + if self.on_command_executed is not None: + # pylint: disable=E1102 + return self.on_command_executed(method_name, data) + + raise IoTError("on_command_executed not set") + + def device_twin_desired_updated(self, desired_property_name: str, desired_property_value, desired_version: int) -> None: + """Called when the device twin is updated + """ + if self.on_property_changed is not None: + # pylint: disable=E1102 + self.on_property_changed(desired_property_name, desired_property_value, desired_version) + + # when a desired property changes, update the reported to match to keep them in sync + self.send_property(desired_property_name, desired_property_value) + + def device_twin_reported_updated(self, reported_property_name: str, reported_property_value, reported_version: int) -> None: + """Called when the device twin is updated + """ + if self.on_property_changed is not None: + # pylint: disable=E1102 + self.on_property_changed(reported_property_name, reported_property_value, reported_version) + + # pylint: disable=R0913 + def __init__( + self, wifi_manager: ESPSPI_WiFiManager, id_scope: str, device_id: str, key: str, token_expires: int = 21600, logger: logging = None + ): + super(IoTCentralDevice, self).__init__() + self._wifi_manager = wifi_manager + self._id_scope = id_scope + self._device_id = device_id + self._key = key + self._token_expires = token_expires + self._logger = logger + self._device_registration = None + self._mqtt = None + + self.on_connection_status_changed = None + self.on_command_executed = None + self.on_property_changed = None + + def connect(self): + """Connects to Azure IoT Central + """ + self._device_registration = DeviceRegistration(self._wifi_manager, self._id_scope, self._device_id, self._key, self._logger) + + token_expiry = int(time.time() + self._token_expires) + hostname = self._device_registration.register_device(token_expiry) + self._mqtt = IoTMQTT(self, hostname, self._device_id, self._key, self._token_expires, self._logger) + + self._mqtt.connect() + + def disconnect(self): + """Disconnects from the MQTT broker + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + self._mqtt.disconnect() + + def is_connected(self) -> bool: + """Gets if there is an open connection to the MQTT broker + """ + if self._mqtt is not None: + return self._mqtt.is_connected() + + return False + + def loop(self): + """Listens for MQTT messages + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + self._mqtt.loop() + + def send_property(self, property_name, data): + """Updates the value of a writable property + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + patch_json = {property_name: data} + patch = json.dumps(patch_json) + self._mqtt.send_twin_patch(patch) + + def send_telemetry(self, data): + """Sends telemetry to the IoT Central app + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + if isinstance(data, dict): + data = json.dumps(data) + + self._mqtt.send_device_to_cloud_message(data) diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py new file mode 100755 index 0000000..2514aae --- /dev/null +++ b/adafruit_azureiot/iothub_device.py @@ -0,0 +1,171 @@ +"""Connectivity to Azure IoT Hub +""" + +import json +from iot_error import IoTError +from iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse +import adafruit_logging as logging + + +def _validate_keys(connection_string_parts): + """Raise ValueError if incorrect combination of keys + """ + host_name = connection_string_parts.get(HOST_NAME) + shared_access_key_name = connection_string_parts.get(SHARED_ACCESS_KEY_NAME) + shared_access_key = connection_string_parts.get(SHARED_ACCESS_KEY) + device_id = connection_string_parts.get(DEVICE_ID) + + if host_name and device_id and shared_access_key: + pass + elif host_name and shared_access_key and shared_access_key_name: + pass + else: + raise ValueError("Invalid Connection String - Incomplete") + + +DELIMITER = ";" +VALUE_SEPARATOR = "=" + +HOST_NAME = "HostName" +SHARED_ACCESS_KEY_NAME = "SharedAccessKeyName" +SHARED_ACCESS_KEY = "SharedAccessKey" +SHARED_ACCESS_SIGNATURE = "SharedAccessSignature" +DEVICE_ID = "DeviceId" +MODULE_ID = "ModuleId" +GATEWAY_HOST_NAME = "GatewayHostName" + +VALID_KEYS = [ + HOST_NAME, + SHARED_ACCESS_KEY_NAME, + SHARED_ACCESS_KEY, + SHARED_ACCESS_SIGNATURE, + DEVICE_ID, + MODULE_ID, + GATEWAY_HOST_NAME, +] + + +class IoTHubDevice(IoTMQTTCallback): + """A device client for the Azure IoT Hub service + """ + + def connection_status_change(self, connected: bool) -> None: + """Called when the connection status changes + """ + if self.on_connection_status_changed is not None: + # pylint: disable=E1102 + self.on_connection_status_changed(connected) + + # pylint: disable=W0613, R0201 + def direct_method_called(self, method_name: str, data) -> IoTResponse: + """Called when a direct method is invoked + """ + if self.on_direct_method_called is not None: + # pylint: disable=E1102 + return self.on_direct_method_called(method_name, data) + + raise IoTError("on_direct_method_called not set") + + # pylint: disable=C0103 + def cloud_to_device_message_received(self, body: str, properties: dict): + """Called when a cloud to device message is received + """ + if self.on_cloud_to_device_message_received is not None: + # pylint: disable=E1102 + self.on_cloud_to_device_message_received(body, properties) + + def device_twin_desired_updated(self, desired_property_name: str, desired_property_value, desired_version: int) -> None: + """Called when the device twin is updated + """ + if self.on_device_twin_desired_updated is not None: + # pylint: disable=E1102 + self.on_device_twin_desired_updated(desired_property_name, desired_property_value, desired_version) + + def device_twin_reported_updated(self, reported_property_name: str, reported_property_value, reported_version: int) -> None: + """Called when the device twin is updated + """ + if self.on_device_twin_reported_updated is not None: + # pylint: disable=E1102 + self.on_device_twin_reported_updated(reported_property_name, reported_property_value, reported_version) + + def __init__(self, device_connection_string: str, token_expires: int = 21600, logger: logging = None): + self._token_expires = token_expires + self._logger = logger if logger is not None else logging.getLogger("log") + + connection_string_values = {} + + try: + cs_args = device_connection_string.split(DELIMITER) + connection_string_values = dict(arg.split(VALUE_SEPARATOR, 1) for arg in cs_args) + except (ValueError, AttributeError): + raise ValueError("Connection string is required and should not be empty or blank and must be supplied as a string") + + if len(cs_args) != len(connection_string_values): + raise ValueError("Invalid Connection String - Unable to parse") + + _validate_keys(connection_string_values) + + self._hostname = connection_string_values[HOST_NAME] + self._device_id = connection_string_values[DEVICE_ID] + self._shared_access_key = connection_string_values[SHARED_ACCESS_KEY] + + self._logger.debug("Hostname: " + self._hostname) + self._logger.debug("Device Id: " + self._device_id) + self._logger.debug("Shared Access Key: " + self._shared_access_key) + + self.on_connection_status_changed = None + self.on_direct_method_called = None + self.on_cloud_to_device_message_received = None + self.on_device_twin_desired_updated = None + self.on_device_twin_reported_updated = None + + self._mqtt = None + + def connect(self): + """Connects to Azure IoT Central + """ + self._mqtt = IoTMQTT(self, self._hostname, self._device_id, self._shared_access_key, self._token_expires, self._logger) + self._mqtt.connect() + + def disconnect(self): + """Disconnects from the MQTT broker + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + self._mqtt.disconnect() + + def is_connected(self) -> bool: + """Gets if there is an open connection to the MQTT broker + """ + if self._mqtt is not None: + return self._mqtt.is_connected() + + return False + + def loop(self): + """Listens for MQTT messages + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + self._mqtt.loop() + + def send_device_to_cloud_message(self, message, system_properties=None): + """Sends a device to cloud message to the IoT Hub + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + self._mqtt.send_device_to_cloud_message(message, system_properties) + + def update_twin(self, patch): + """Updates the reported properties in the devices device twin + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + if isinstance(patch, dict): + patch = json.dumps(patch) + + self._mqtt.send_twin_patch(patch) diff --git a/docs/conf.py b/docs/conf.py index bef783e..5bfec8d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -# autodoc_mock_imports = ["digitalio", "busio"] +autodoc_mock_imports = ["adafruit_loging"] intersphinx_mapping = { diff --git a/examples/azureiot_device_management.py b/examples/azureiot_device_management.py deleted file mode 100644 index 28fed04..0000000 --- a/examples/azureiot_device_management.py +++ /dev/null @@ -1,44 +0,0 @@ -import board -import busio -from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel -from adafruit_azureiot import IOT_Hub - -# 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 - -# ESP32 Setup -try: - esp32_cs = DigitalInOut(board.ESP_CS) - esp32_ready = DigitalInOut(board.ESP_BUSY) - esp32_reset = DigitalInOut(board.ESP_RESET) -except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) -spi = busio.SPI(board.SCK, board.MOSI, board.MISO) -esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2 -) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) - -# Create an instance of the Azure IoT Hub -hub = IOT_Hub( - wifi, secrets["azure_iot_hub"], secrets["azure_iot_sas"], secrets["device_id"] -) - -# Enumerate all devices on an Azure IoT Hub -all_hub_devices = hub.get_devices() -print(all_hub_devices) - -# Get a specified device on an Azure IoT Hub -device_data = hub.get_device() -print(device_data) diff --git a/examples/azureiot_devicetwin.py b/examples/azureiot_devicetwin.py deleted file mode 100644 index 40d8757..0000000 --- a/examples/azureiot_devicetwin.py +++ /dev/null @@ -1,53 +0,0 @@ -import board -import busio -from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel -from adafruit_azureiot import IOT_Hub - -# 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 - -# ESP32 Setup -try: - esp32_cs = DigitalInOut(board.ESP_CS) - esp32_ready = DigitalInOut(board.ESP_BUSY) - esp32_reset = DigitalInOut(board.ESP_RESET) -except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) -spi = busio.SPI(board.SCK, board.MOSI, board.MISO) -esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2 -) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) - -# Create an instance of the Azure IoT Hub -hub = IOT_Hub( - wifi, secrets["azure_iot_hub"], secrets["azure_iot_sas"], secrets["device_id"] -) - -# Get a Device Twin -device_twin = hub.get_device_twin() -# Filter out the device's name from the twin's properties -device_name = device_twin["properties"]["desired"]["deviceName"] -print(device_name) - -# Update a Device Twin's Properties -data = { - "properties": {"desired": {"deviceName": "{{BasementTemperatureLoggerFeather}}"}} -} -hub.update_device_twin(data) - -# And read the updated device twin information -device_twin = hub.get_device_twin() -device_name = device_twin["properties"]["desired"]["deviceName"] -print(device_name) diff --git a/examples/iotcentral_commands.py b/examples/iotcentral_commands.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/iotcentral_properties.py b/examples/iotcentral_properties.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/azureiot_simpletest.py b/examples/iotcentral_simpletest.py similarity index 100% rename from examples/azureiot_simpletest.py rename to examples/iotcentral_simpletest.py diff --git a/examples/iotcentral_telemetry.py b/examples/iotcentral_telemetry.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/iothub_directmethods.py b/examples/iothub_directmethods.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/iothub_messages.py b/examples/iothub_messages.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/iothub_simpletest.py b/examples/iothub_simpletest.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/iothub_twin_operations.py b/examples/iothub_twin_operations.py new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index bdc2de0..ffec0c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,6 @@ Adafruit-Blinka -Adafruit_CircuitPython_ESP32SPI \ No newline at end of file +Adafruit_CircuitPython_ESP32SPI +Adafruit-CircuitPython-miniMQTT +CircuitPython-HMAC +CircuitPython-Base64 +CircuitPython-Parse \ No newline at end of file diff --git a/setup.py b/setup.py index 9505c57..1e9e0cd 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ # Author details author="Adafruit Industries", author_email="circuitpython@adafruit.com", - install_requires=["Adafruit-Blinka", "Adafruit_CircuitPython_ESP32SPI"], + install_requires=["Adafruit-Blinka", "Adafruit_CircuitPython_ESP32SPI", "Adafruit-CircuitPython-miniMQTT", "CircuitPython-HMAC", "CircuitPython-Base64", "CircuitPython-Parse"], # Choose your license license="MIT", # See https://pypi.python.org/pypi?%3Aaction=list_classifiers @@ -44,7 +44,7 @@ "Programming Language :: Python :: 3.5", ], # What does your project relate to? - keywords="adafruit blinka circuitpython micropython azureiot azure, iot, device, services", + keywords="adafruit blinka circuitpython micropython azureiot azure iot device services, iothub, iotcentral", # You can just specify the packages manually here if your project is # simple. Or you can use find_packages(). # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, From 0a0159138915bcf42ecdb4f2ffa5897cce30c432 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sat, 11 Apr 2020 14:49:23 -0700 Subject: [PATCH 02/33] Upping line length --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6977a9..2d2424c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,7 +45,7 @@ jobs: run: git describe --dirty --always --tags - name: Check formatting run: | - black --check --target-version=py35 . + black --check --target-version=py35 --line-length=140 . - name: PyLint run: | pylint $( find . -path './adafruit*.py' ) From 4f6b3fe3c64b0eae7a173643563a4f278f5b917a Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sat, 11 Apr 2020 14:53:19 -0700 Subject: [PATCH 03/33] Black formatting --- docs/conf.py | 18 ++---------------- examples/iotcentral_simpletest.py | 8 ++------ setup.py | 9 ++++++++- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5bfec8d..81e949d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -137,28 +137,14 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ( - master_doc, - "AdafruitAzureIoTLibrary.tex", - "AdafruitAzureIoT Library Documentation", - author, - "manual", - ), + (master_doc, "AdafruitAzureIoTLibrary.tex", "AdafruitAzureIoT Library Documentation", author, "manual",), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ( - master_doc, - "AdafruitAzureIoTlibrary", - "Adafruit AzureIoT Library Documentation", - [author], - 1, - ) -] +man_pages = [(master_doc, "AdafruitAzureIoTlibrary", "Adafruit AzureIoT Library Documentation", [author], 1,)] # -- Options for Texinfo output ------------------------------------------- diff --git a/examples/iotcentral_simpletest.py b/examples/iotcentral_simpletest.py index 87910f5..85e2947 100644 --- a/examples/iotcentral_simpletest.py +++ b/examples/iotcentral_simpletest.py @@ -24,17 +24,13 @@ esp32_reset = DigitalInOut(board.D5) spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2 -) # Uncomment for Most Boards +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards """Uncomment below for ItsyBitsy M4""" # status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) # Create an instance of the Azure IoT Hub -hub = IOT_Hub( - wifi, secrets["azure_iot_hub"], secrets["azure_iot_sas"], secrets["device_id"] -) +hub = IOT_Hub(wifi, secrets["azure_iot_hub"], secrets["azure_iot_sas"], secrets["device_id"]) # Send a Device-to-Cloud message print("Sending Data to Azure IoT Hub...") diff --git a/setup.py b/setup.py index 1e9e0cd..8cc5055 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,14 @@ # Author details author="Adafruit Industries", author_email="circuitpython@adafruit.com", - install_requires=["Adafruit-Blinka", "Adafruit_CircuitPython_ESP32SPI", "Adafruit-CircuitPython-miniMQTT", "CircuitPython-HMAC", "CircuitPython-Base64", "CircuitPython-Parse"], + install_requires=[ + "Adafruit-Blinka", + "Adafruit_CircuitPython_ESP32SPI", + "Adafruit-CircuitPython-miniMQTT", + "CircuitPython-HMAC", + "CircuitPython-Base64", + "CircuitPython-Parse", + ], # Choose your license license="MIT", # See https://pypi.python.org/pypi?%3Aaction=list_classifiers From 89db97392427b37ecb218fe3760dca8493ebeadb Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sat, 11 Apr 2020 14:57:15 -0700 Subject: [PATCH 04/33] Update iotcentral_simpletest.py --- examples/iotcentral_simpletest.py | 49 ------------------------------- 1 file changed, 49 deletions(-) diff --git a/examples/iotcentral_simpletest.py b/examples/iotcentral_simpletest.py index 85e2947..e69de29 100644 --- a/examples/iotcentral_simpletest.py +++ b/examples/iotcentral_simpletest.py @@ -1,49 +0,0 @@ -from random import randint -import board -import busio -from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel -from adafruit_azureiot import IOT_Hub - -# 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 - -# ESP32 Setup -try: - esp32_cs = DigitalInOut(board.ESP_CS) - esp32_ready = DigitalInOut(board.ESP_BUSY) - esp32_reset = DigitalInOut(board.ESP_RESET) -except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) -spi = busio.SPI(board.SCK, board.MOSI, board.MISO) -esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) - -# Create an instance of the Azure IoT Hub -hub = IOT_Hub(wifi, secrets["azure_iot_hub"], secrets["azure_iot_sas"], secrets["device_id"]) - -# Send a Device-to-Cloud message -print("Sending Data to Azure IoT Hub...") -data = randint(0, 100) -hub.send_device_message(str(data)) -print("Data Sent!") - -# Receive a Cloud-to-Device message -# NOTE: HTTP Cloud-to-Device messages are HEAVILY throttled over HTTP. -# Microsoft suggests a polling interval of the below code for every 25 minutes. -print("Receiving a message from an Azure IoT Hub...") -message = hub.get_hub_message() -if message is None: - print("IoT Hub Message Queue is empty!") -else: - print(message) From 707a7edfaaef2b295c8677ead49b2666fc17b4fe Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sun, 12 Apr 2020 14:14:18 -0700 Subject: [PATCH 05/33] Addin exports to __init__ --- adafruit_azureiot/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/adafruit_azureiot/__init__.py b/adafruit_azureiot/__init__.py index 131aeb1..90b2574 100644 --- a/adafruit_azureiot/__init__.py +++ b/adafruit_azureiot/__init__.py @@ -42,5 +42,11 @@ * Community Parse library: https://github.com/jimbobbennett/CircuitPython_Parse """ +from .iot_mqtt import IoTResponse +from .iotcentral_device import IoTCentralDevice +from .iothub_device import IoTHubDevice + __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AzureIoT.git" + +__all__ = ["IoTHubDevice", "IoTCentralDevice", "IoTResponse"] From 5bd2f83a3ccfa9bf7573e01677f1c298dd77b735 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sun, 12 Apr 2020 14:43:13 -0700 Subject: [PATCH 06/33] Passing wifi manager around to set the socket --- .pylintrc | 2 +- adafruit_azureiot/iot_mqtt.py | 16 +++++++++++++++- adafruit_azureiot/iotcentral_device.py | 3 +-- adafruit_azureiot/iothub_device.py | 8 ++++++-- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/.pylintrc b/.pylintrc index 2695033..bef1f5a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -399,7 +399,7 @@ max-args=6 # Maximum number of attributes for a class (see R0902). # max-attributes=7 -max-attributes=11 +max-attributes=12 # Maximum number of boolean expressions in a if statement max-bool-expr=5 diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 0223d46..2e0b424 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -4,6 +4,9 @@ import gc import json import time +import adafruit_esp32spi.adafruit_esp32spi_socket as socket +from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager +import adafruit_minimqtt as minimqtt from adafruit_minimqtt import MQTT import circuitpython_parse as parse from device_registration import DeviceRegistration @@ -85,6 +88,8 @@ def _gen_sas_token(self): # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/25 def _try_create_mqtt_client(self, hostname): + minimqtt.set_socket(socket, self._wifi_manager.esp) + self._mqtts = MQTT( broker=hostname, username=self._username, @@ -298,9 +303,17 @@ def _get_device_settings(self) -> None: # pylint: disable=R0913 def __init__( - self, callback: IoTMQTTCallback, hostname: str, device_id: str, key: str, token_expires: int = 21600, logger: logging = None + self, + callback: IoTMQTTCallback, + wifi_manager: ESPSPI_WiFiManager, + hostname: str, + device_id: str, + key: str, + token_expires: int = 21600, + logger: logging = None, ): """Create the Azure IoT MQTT client + :param wifi_manager: The WiFi manager :param IoTMQTTCallback callback: A callback class :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 @@ -309,6 +322,7 @@ def __init__( :param adafruit_logging logger: The logger """ self._callback = callback + self._wifi_manager = wifi_manager self._mqtt_connected = False self._auth_response_received = False self._mqtts = None diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index 29adf54..6b74d14 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -52,7 +52,6 @@ def device_twin_reported_updated(self, reported_property_name: str, reported_pro def __init__( self, wifi_manager: ESPSPI_WiFiManager, id_scope: str, device_id: str, key: str, token_expires: int = 21600, logger: logging = None ): - super(IoTCentralDevice, self).__init__() self._wifi_manager = wifi_manager self._id_scope = id_scope self._device_id = device_id @@ -73,7 +72,7 @@ def connect(self): token_expiry = int(time.time() + self._token_expires) hostname = self._device_registration.register_device(token_expiry) - self._mqtt = IoTMQTT(self, hostname, self._device_id, self._key, self._token_expires, self._logger) + self._mqtt = IoTMQTT(self, self._wifi_manager, hostname, self._device_id, self._key, self._token_expires, self._logger) self._mqtt.connect() diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index 2514aae..d68b668 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -2,6 +2,7 @@ """ import json +from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager from iot_error import IoTError from iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse import adafruit_logging as logging @@ -88,7 +89,8 @@ def device_twin_reported_updated(self, reported_property_name: str, reported_pro # pylint: disable=E1102 self.on_device_twin_reported_updated(reported_property_name, reported_property_value, reported_version) - def __init__(self, device_connection_string: str, token_expires: int = 21600, logger: logging = None): + def __init__(self, wifi_manager: ESPSPI_WiFiManager, device_connection_string: str, token_expires: int = 21600, logger: logging = None): + self._wifi_manager = wifi_manager self._token_expires = token_expires self._logger = logger if logger is not None else logging.getLogger("log") @@ -124,7 +126,9 @@ def __init__(self, device_connection_string: str, token_expires: int = 21600, lo def connect(self): """Connects to Azure IoT Central """ - self._mqtt = IoTMQTT(self, self._hostname, self._device_id, self._shared_access_key, self._token_expires, self._logger) + self._mqtt = IoTMQTT( + self, self._wifi_manager, self._hostname, self._device_id, self._shared_access_key, self._token_expires, self._logger + ) self._mqtt.connect() def disconnect(self): From 359adcf9a894a262a964552ccfb145fe59e195fb Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sun, 12 Apr 2020 15:18:26 -0700 Subject: [PATCH 07/33] Added IoT Hub examples --- .github/workflows/build.yml | 2 +- adafruit_azureiot/iot_mqtt.py | 2 +- adafruit_azureiot/iotcentral_device.py | 4 +- examples/iothub_directmethods.py | 75 +++++++++++++++++++++++ examples/iothub_messages.py | 85 ++++++++++++++++++++++++++ examples/iothub_simpletest.py | 75 +++++++++++++++++++++++ examples/iothub_twin_operations.py | 85 ++++++++++++++++++++++++++ 7 files changed, 324 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2d2424c..43e3d14 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: - name: PyLint run: | pylint $( find . -path './adafruit*.py' ) - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace $( find . -path "./examples/*.py" )) + ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace,wrong-import-position $( find . -path "./examples/*.py" )) - name: Build assets run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - name: Build docs diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 2e0b424..a497d6f 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -46,7 +46,7 @@ def connection_status_change(self, connected: bool) -> None: """ # pylint: disable=W0613, R0201 - def direct_method_called(self, method_name: str, data) -> IoTResponse: + def direct_method_called(self, method_name: str, payload) -> IoTResponse: """Called when a direct method is invoked """ return IoTResponse("", "") diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index 6b74d14..40ab25e 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -22,12 +22,12 @@ def connection_status_change(self, connected: bool) -> None: self.on_connection_status_changed(connected) # pylint: disable=W0613, R0201 - def direct_method_called(self, method_name: str, data) -> IoTResponse: + def direct_method_called(self, method_name: str, payload) -> IoTResponse: """Called when a direct method is invoked """ if self.on_command_executed is not None: # pylint: disable=E1102 - return self.on_command_executed(method_name, data) + return self.on_command_executed(method_name, payload) raise IoTError("on_command_executed not set") diff --git a/examples/iothub_directmethods.py b/examples/iothub_directmethods.py index e69de29..a0eafa4 100644 --- a/examples/iothub_directmethods.py +++ b/examples/iothub_directmethods.py @@ -0,0 +1,75 @@ +import time +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import neopixel + +# 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 + +# ESP32 Setup +try: + esp32_cs = DigitalInOut(board.ESP_CS) + esp32_ready = DigitalInOut(board.ESP_BUSY) + esp32_reset = DigitalInOut(board.ESP_RESET) +except AttributeError: + esp32_cs = DigitalInOut(board.D9) + esp32_ready = DigitalInOut(board.D10) + esp32_reset = DigitalInOut(board.D5) +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +# You will need an Azure subscription to create an IoT Hub +# +# 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 promary connection string. +# Add it to the secrets.py file in an entry called DeviceConnectionString + +from adafruit_azureiot import IoTHubDevice +from adafruit_azureiot.iot_mqtt import IoTResponse + +# Create an IoT Hub device client and connect +device = IoTHubDevice(wifi, secrets["DeviceConnectionString"]) + +# 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 messagge +def direct_method_called(method_name: str, payload) -> IoTResponse: + print("Received direct method", method_name, "with data", str(payload)) + return IoTResponse(200, "OK") + + +device.on_direct_method_called = direct_method_called + +device.connect() + +while True: + # Poll every second for messages from the cloud + device.loop() + + time.sleep(1) diff --git a/examples/iothub_messages.py b/examples/iothub_messages.py index e69de29..5f88064 100644 --- a/examples/iothub_messages.py +++ b/examples/iothub_messages.py @@ -0,0 +1,85 @@ +import json +import random +import time +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import neopixel + +# 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 + +# ESP32 Setup +try: + esp32_cs = DigitalInOut(board.ESP_CS) + esp32_ready = DigitalInOut(board.ESP_BUSY) + esp32_reset = DigitalInOut(board.ESP_RESET) +except AttributeError: + esp32_cs = DigitalInOut(board.D9) + esp32_ready = DigitalInOut(board.D10) + esp32_reset = DigitalInOut(board.D5) +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +# You will need an Azure subscription to create an IoT Hub +# +# 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 promary connection string. +# Add it to the secrets.py file in an entry called DeviceConnectionString + +from adafruit_azureiot import IoTHubDevice + +# Create an IoT Hub device client and connect +device = IoTHubDevice(wifi, secrets["DeviceConnectionString"]) + +# 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)) + + +device.on_cloud_to_device_message_received = cloud_to_device_message_received + +device.connect() + +message_counter = 0 + +while True: + # 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 + + message_counter = message_counter + 1 + + # Poll every second for messages from the cloud + device.loop() + + time.sleep(1) diff --git a/examples/iothub_simpletest.py b/examples/iothub_simpletest.py index e69de29..a43a434 100644 --- a/examples/iothub_simpletest.py +++ b/examples/iothub_simpletest.py @@ -0,0 +1,75 @@ +import json +import random +import time +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import neopixel + +# 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 + +# ESP32 Setup +try: + esp32_cs = DigitalInOut(board.ESP_CS) + esp32_ready = DigitalInOut(board.ESP_BUSY) + esp32_reset = DigitalInOut(board.ESP_RESET) +except AttributeError: + esp32_cs = DigitalInOut(board.D9) + esp32_ready = DigitalInOut(board.D10) + esp32_reset = DigitalInOut(board.D5) +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +# You will need an Azure subscription to create an IoT Hub +# +# 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 promary connection string. +# Add it to the secrets.py file in an entry called DeviceConnectionString + +from adafruit_azureiot import IoTHubDevice + +# Create an IoT Hub device client and connect +device = IoTHubDevice(wifi, secrets["DeviceConnectionString"]) +device.connect() + +message_counter = 0 + +while True: + # 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 + + message_counter = message_counter + 1 + + # Poll every second for messages from the cloud + device.loop() + + time.sleep(1) diff --git a/examples/iothub_twin_operations.py b/examples/iothub_twin_operations.py index e69de29..5d4af76 100644 --- a/examples/iothub_twin_operations.py +++ b/examples/iothub_twin_operations.py @@ -0,0 +1,85 @@ +import random +import time +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import neopixel + +# 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 + +# ESP32 Setup +try: + esp32_cs = DigitalInOut(board.ESP_CS) + esp32_ready = DigitalInOut(board.ESP_BUSY) + esp32_reset = DigitalInOut(board.ESP_RESET) +except AttributeError: + esp32_cs = DigitalInOut(board.D9) + esp32_ready = DigitalInOut(board.D10) + esp32_reset = DigitalInOut(board.D5) +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +# You will need an Azure subscription to create an IoT Hub +# +# 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 promary connection string. +# Add it to the secrets.py file in an entry called DeviceConnectionString + +from adafruit_azureiot import IoTHubDevice + +# Create an IoT Hub device client and connect +device = IoTHubDevice(wifi, secrets["DeviceConnectionString"]) + +# 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) + + +device.on_device_twin_desired_updated = device_twin_desired_updated + +device.connect() + +message_counter = 0 + +while True: + 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 + + message_counter = message_counter + 1 + + # Poll every second for messages from the cloud + device.loop() + + time.sleep(1) From 0a7d52cf36aaddd367614916b47f93cbb0b8c804 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sun, 12 Apr 2020 15:51:03 -0700 Subject: [PATCH 08/33] Added iot central examples --- examples/iotcentral_commands.py | 80 +++++++++++++++++++++++++++ examples/iotcentral_properties.py | 87 ++++++++++++++++++++++++++++++ examples/iotcentral_simpletest.py | 79 +++++++++++++++++++++++++++ examples/iotcentral_telemetry.py | 0 examples/iothub_directmethods.py | 6 +-- examples/iothub_messages.py | 4 +- examples/iothub_simpletest.py | 4 +- examples/iothub_twin_operations.py | 4 +- 8 files changed, 255 insertions(+), 9 deletions(-) delete mode 100644 examples/iotcentral_telemetry.py diff --git a/examples/iotcentral_commands.py b/examples/iotcentral_commands.py index e69de29..a65cbfd 100644 --- a/examples/iotcentral_commands.py +++ b/examples/iotcentral_commands.py @@ -0,0 +1,80 @@ +import time +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import neopixel + +# 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 + +# ESP32 Setup +try: + esp32_cs = DigitalInOut(board.ESP_CS) + esp32_ready = DigitalInOut(board.ESP_BUSY) + esp32_reset = DigitalInOut(board.ESP_RESET) +except AttributeError: + esp32_cs = DigitalInOut(board.D9) + esp32_ready = DigitalInOut(board.D10) + esp32_reset = DigitalInOut(board.D5) +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +# 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 + +from adafruit_azureiot import IoTCentralDevice +from adafruit_azureiot.iot_mqtt import IoTResponse + +# Create an IoT Hub device client and connect +device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["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 IoTResponse(200, "OK") + + +device.on_command_executed = command_executed + +device.connect() + +while True: + # Poll every second for messages from the cloud + device.loop() + + time.sleep(1) diff --git a/examples/iotcentral_properties.py b/examples/iotcentral_properties.py index e69de29..66f417f 100644 --- a/examples/iotcentral_properties.py +++ b/examples/iotcentral_properties.py @@ -0,0 +1,87 @@ +import random +import time +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import neopixel + +# 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 + +# ESP32 Setup +try: + esp32_cs = DigitalInOut(board.ESP_CS) + esp32_ready = DigitalInOut(board.ESP_BUSY) + esp32_reset = DigitalInOut(board.ESP_RESET) +except AttributeError: + esp32_cs = DigitalInOut(board.D9) + esp32_ready = DigitalInOut(board.D10) + esp32_reset = DigitalInOut(board.D5) +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +# 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 + +from adafruit_azureiot import IoTCentralDevice + +# Create an IoT Hub device client and connect +device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["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)) + + +device.on_property_changed = property_changed + +device.connect() + +message_counter = 0 + +while True: + # Send property values every minute + # You can see the values in the devices dashboard + if message_counter > 60: + device.send_property("Temperature", random.randint(0, 50)) + message_counter = 0 + + message_counter = message_counter + 1 + + # Poll every second for messages from the cloud + device.loop() + + time.sleep(1) diff --git a/examples/iotcentral_simpletest.py b/examples/iotcentral_simpletest.py index e69de29..2f98b03 100644 --- a/examples/iotcentral_simpletest.py +++ b/examples/iotcentral_simpletest.py @@ -0,0 +1,79 @@ +import json +import random +import time +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import neopixel + +# 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 + +# ESP32 Setup +try: + esp32_cs = DigitalInOut(board.ESP_CS) + esp32_ready = DigitalInOut(board.ESP_BUSY) + esp32_reset = DigitalInOut(board.ESP_RESET) +except AttributeError: + esp32_cs = DigitalInOut(board.D9) + esp32_ready = DigitalInOut(board.D10) + esp32_reset = DigitalInOut(board.D5) +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +# 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 + +from adafruit_azureiot import IoTCentralDevice + +# Create an IoT Hub device client and connect +device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"]) +device.connect() + +message_counter = 0 + +while True: + # 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 + + message_counter = message_counter + 1 + + # Poll every second for messages from the cloud + device.loop() + + time.sleep(1) diff --git a/examples/iotcentral_telemetry.py b/examples/iotcentral_telemetry.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/iothub_directmethods.py b/examples/iothub_directmethods.py index a0eafa4..b9d0d8d 100644 --- a/examples/iothub_directmethods.py +++ b/examples/iothub_directmethods.py @@ -28,7 +28,7 @@ # status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) -# You will need an Azure subscription to create an IoT Hub +# You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: # @@ -45,7 +45,7 @@ # 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 promary connection string. +# 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 DeviceConnectionString from adafruit_azureiot import IoTHubDevice @@ -58,7 +58,7 @@ # 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 messagge +# successfully or not, returning an HTTP status code and message def direct_method_called(method_name: str, payload) -> IoTResponse: print("Received direct method", method_name, "with data", str(payload)) return IoTResponse(200, "OK") diff --git a/examples/iothub_messages.py b/examples/iothub_messages.py index 5f88064..5ef27b1 100644 --- a/examples/iothub_messages.py +++ b/examples/iothub_messages.py @@ -30,7 +30,7 @@ # status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) -# You will need an Azure subscription to create an IoT Hub +# You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: # @@ -47,7 +47,7 @@ # 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 promary connection string. +# 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 DeviceConnectionString from adafruit_azureiot import IoTHubDevice diff --git a/examples/iothub_simpletest.py b/examples/iothub_simpletest.py index a43a434..025281b 100644 --- a/examples/iothub_simpletest.py +++ b/examples/iothub_simpletest.py @@ -30,7 +30,7 @@ # status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) -# You will need an Azure subscription to create an IoT Hub +# You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: # @@ -47,7 +47,7 @@ # 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 promary connection string. +# 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 DeviceConnectionString from adafruit_azureiot import IoTHubDevice diff --git a/examples/iothub_twin_operations.py b/examples/iothub_twin_operations.py index 5d4af76..c4e528c 100644 --- a/examples/iothub_twin_operations.py +++ b/examples/iothub_twin_operations.py @@ -29,7 +29,7 @@ # status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) -# You will need an Azure subscription to create an IoT Hub +# You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: # @@ -46,7 +46,7 @@ # 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 promary connection string. +# 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 DeviceConnectionString from adafruit_azureiot import IoTHubDevice From 5cbd59e0ee77d3b885b7dd365539b8aed5970681 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sun, 12 Apr 2020 16:22:22 -0700 Subject: [PATCH 09/33] Trying to get sphinx to not error --- adafruit_azureiot/__init__.py | 3 +- docs/conf.py | 6 +-- docs/examples.rst | 53 ++++++++++++++++++++-- examples/iotcentral_notconnected.py | 69 +++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 examples/iotcentral_notconnected.py diff --git a/adafruit_azureiot/__init__.py b/adafruit_azureiot/__init__.py index 90b2574..d631969 100644 --- a/adafruit_azureiot/__init__.py +++ b/adafruit_azureiot/__init__.py @@ -42,6 +42,7 @@ * Community Parse library: https://github.com/jimbobbennett/CircuitPython_Parse """ +from .iot_error import IoTError from .iot_mqtt import IoTResponse from .iotcentral_device import IoTCentralDevice from .iothub_device import IoTHubDevice @@ -49,4 +50,4 @@ __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AzureIoT.git" -__all__ = ["IoTHubDevice", "IoTCentralDevice", "IoTResponse"] +__all__ = ["IoTHubDevice", "IoTCentralDevice", "IoTResponse", "IoTError"] diff --git a/docs/conf.py b/docs/conf.py index 81e949d..b6ea22d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = ["adafruit_loging"] +autodoc_mock_imports = ["adafruit_logging", "device_registration", "iot_mqtt", "iot_error"] intersphinx_mapping = { @@ -39,8 +39,8 @@ # General information about the project. project = "Adafruit AzureIoT Library" -copyright = "2019 Brent Rubell" -author = "Brent Rubell" +copyright = "2019 Brent Rubell, Jim Bennett, Elena Horton" +author = "Brent Rubell, Jim Bennett, Elena Horton" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/examples.rst b/docs/examples.rst index 839a280..013a910 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,8 +1,53 @@ -Simple test +IoT Hub ------------ -Ensure your device works with this simple test. +Ensure your IoT Hub device works with this simple test. -.. literalinclude:: ../examples/azureiot_simpletest.py - :caption: examples/azureiot_simpletest.py +.. literalinclude:: ../examples/iothub_simpletest.py + :caption: examples/iothub_simpletest.py :linenos: + +Handle direct methods. + +.. literalinclude:: ../examples/iothub_directmethods.py + :caption: examples/iothub_directmethods.py + :linenos: + +Send device to cloud messages, and handle cloud to device messages. + +.. literalinclude:: ../examples/iothub_messages.py + :caption: examples/iothub_messages.py + :linenos: + +Update the reported properties of the devices device twin, and receive updates to desired properties. + +.. literalinclude:: ../examples/iothub_twin_operations.py + :caption: examples/iothub_twin_operations.py + :linenos: + +IoT Central +------------ + +Ensure your IoT Central device works with this simple test. + +.. literalinclude:: ../examples/iotcentral_simpletest.py + :caption: examples/iotcentral_simpletest.py + :linenos: + +Handle commands. + +.. literalinclude:: ../examples/iotcentral_commands.py + :caption: examples/iotcentral_commands.py + :linenos: + +Update the properties of the device, and receive updates to properties. + +.. literalinclude:: ../examples/iotcentral_properties.py + :caption: examples/iotcentral_properties.py + :linenos: + +Handle connection errors. + +.. literalinclude:: ../examples/iotcentral_notconnected.py + :caption: examples/iotcentral_notconnected.py + :linenos: \ No newline at end of file diff --git a/examples/iotcentral_notconnected.py b/examples/iotcentral_notconnected.py new file mode 100644 index 0000000..31040fe --- /dev/null +++ b/examples/iotcentral_notconnected.py @@ -0,0 +1,69 @@ +import json +import random +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import neopixel + +# 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 + +# ESP32 Setup +try: + esp32_cs = DigitalInOut(board.ESP_CS) + esp32_ready = DigitalInOut(board.ESP_BUSY) + esp32_reset = DigitalInOut(board.ESP_RESET) +except AttributeError: + esp32_cs = DigitalInOut(board.D9) + esp32_ready = DigitalInOut(board.D10) + esp32_reset = DigitalInOut(board.D5) +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +# 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 + +from adafruit_azureiot import IoTCentralDevice, IoTError + +# Create an IoT Hub device client and connect +device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["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) From f683715cc6e701b60187ebabff2e1b60e2e978f4 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sun, 12 Apr 2020 16:25:22 -0700 Subject: [PATCH 10/33] Update conf.py --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index b6ea22d..24961b2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = ["adafruit_logging", "device_registration", "iot_mqtt", "iot_error"] +autodoc_mock_imports = ["adafruit_logging", "adafruit_requests", "device_registration", "iot_mqtt", "iot_error"] intersphinx_mapping = { From 8b3baba4181047be3cc0e753d7a13518cdacb296 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Sun, 12 Apr 2020 18:17:13 -0700 Subject: [PATCH 11/33] Makin examples work --- adafruit_azureiot/iot_mqtt.py | 2 +- adafruit_azureiot/iotcentral_device.py | 6 ++--- adafruit_azureiot/iothub_device.py | 8 +++--- docs/conf.py | 2 +- examples/iotcentral_commands.py | 23 +++++++++++------ examples/iotcentral_notconnected.py | 24 ++++++++++++------ examples/iotcentral_properties.py | 31 ++++++++++++++--------- examples/iotcentral_simpletest.py | 31 ++++++++++++++--------- examples/iothub_directmethods.py | 27 ++++++++++++-------- examples/iothub_messages.py | 35 +++++++++++++++----------- examples/iothub_simpletest.py | 35 +++++++++++++++----------- examples/iothub_twin_operations.py | 35 +++++++++++++++----------- 12 files changed, 158 insertions(+), 101 deletions(-) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index a497d6f..896f855 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -9,8 +9,8 @@ import adafruit_minimqtt as minimqtt from adafruit_minimqtt import MQTT import circuitpython_parse as parse -from device_registration import DeviceRegistration import adafruit_logging as logging +from .device_registration import DeviceRegistration from . import constants diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index 40ab25e..733fff9 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -4,10 +4,10 @@ import json import time from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager -from device_registration import DeviceRegistration -from iot_error import IoTError -from iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse import adafruit_logging as logging +from .device_registration import DeviceRegistration +from .iot_error import IoTError +from .iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse class IoTCentralDevice(IoTMQTTCallback): diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index d68b668..c1bc477 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -3,9 +3,9 @@ import json from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager -from iot_error import IoTError -from iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse import adafruit_logging as logging +from .iot_error import IoTError +from .iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse def _validate_keys(connection_string_parts): @@ -58,12 +58,12 @@ def connection_status_change(self, connected: bool) -> None: self.on_connection_status_changed(connected) # pylint: disable=W0613, R0201 - def direct_method_called(self, method_name: str, data) -> IoTResponse: + def direct_method_called(self, method_name: str, payload) -> IoTResponse: """Called when a direct method is invoked """ if self.on_direct_method_called is not None: # pylint: disable=E1102 - return self.on_direct_method_called(method_name, data) + return self.on_direct_method_called(method_name, payload) raise IoTError("on_direct_method_called not set") diff --git a/docs/conf.py b/docs/conf.py index 24961b2..41677b7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = ["adafruit_logging", "adafruit_requests", "device_registration", "iot_mqtt", "iot_error"] +autodoc_mock_imports = ["adafruit_logging", "adafruit_requests", "adafruit_hashlib", "adafruit_ntp"] intersphinx_mapping = { diff --git a/examples/iotcentral_commands.py b/examples/iotcentral_commands.py index a65cbfd..f564ff5 100644 --- a/examples/iotcentral_commands.py +++ b/examples/iotcentral_commands.py @@ -3,7 +3,7 @@ import busio from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel +from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file try: @@ -18,15 +18,22 @@ esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) + esp32_cs = DigitalInOut(board.D13) + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) + spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi.connect() + +ntp = NTP(esp) +while not ntp.valid_time: + ntp.set_time() + + if not ntp.valid_time: + time.sleep(1) # 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, diff --git a/examples/iotcentral_notconnected.py b/examples/iotcentral_notconnected.py index 31040fe..04fad30 100644 --- a/examples/iotcentral_notconnected.py +++ b/examples/iotcentral_notconnected.py @@ -1,10 +1,11 @@ import json import random +import time import board import busio from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel +from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file try: @@ -19,15 +20,22 @@ esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) + esp32_cs = DigitalInOut(board.D13) + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) + spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi.connect() + +ntp = NTP(esp) +while not ntp.valid_time: + ntp.set_time() + + if not ntp.valid_time: + time.sleep(1) # 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, diff --git a/examples/iotcentral_properties.py b/examples/iotcentral_properties.py index 66f417f..11014b4 100644 --- a/examples/iotcentral_properties.py +++ b/examples/iotcentral_properties.py @@ -4,7 +4,7 @@ import busio from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel +from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file try: @@ -19,15 +19,22 @@ esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) + esp32_cs = DigitalInOut(board.D13) + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) + spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi.connect() + +ntp = NTP(esp) +while not ntp.valid_time: + ntp.set_time() + + if not ntp.valid_time: + time.sleep(1) # 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, @@ -70,16 +77,16 @@ def property_changed(property_name, property_value, version): device.connect() -message_counter = 0 +message_counter = 60 while True: # Send property values every minute # You can see the values in the devices dashboard - if message_counter > 60: + if message_counter >= 60: device.send_property("Temperature", random.randint(0, 50)) message_counter = 0 - - message_counter = message_counter + 1 + else: + message_counter = message_counter + 1 # Poll every second for messages from the cloud device.loop() diff --git a/examples/iotcentral_simpletest.py b/examples/iotcentral_simpletest.py index 2f98b03..6cfa122 100644 --- a/examples/iotcentral_simpletest.py +++ b/examples/iotcentral_simpletest.py @@ -5,7 +5,7 @@ import busio from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel +from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file try: @@ -20,15 +20,22 @@ esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) + esp32_cs = DigitalInOut(board.D13) + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) + spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi.connect() + +ntp = NTP(esp) +while not ntp.valid_time: + ntp.set_time() + + if not ntp.valid_time: + time.sleep(1) # 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, @@ -61,17 +68,17 @@ device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"]) device.connect() -message_counter = 0 +message_counter = 60 while True: # Send telemetry every minute # You can see the values in the devices dashboard - if message_counter > 60: + if message_counter >= 60: message = {"Temperature": random.randint(0, 50)} device.send_telemetry(json.dumps(message)) message_counter = 0 - - message_counter = message_counter + 1 + else: + message_counter = message_counter + 1 # Poll every second for messages from the cloud device.loop() diff --git a/examples/iothub_directmethods.py b/examples/iothub_directmethods.py index b9d0d8d..ec0a4f3 100644 --- a/examples/iothub_directmethods.py +++ b/examples/iothub_directmethods.py @@ -3,7 +3,7 @@ import busio from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel +from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file try: @@ -18,15 +18,22 @@ esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) + esp32_cs = DigitalInOut(board.D13) + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) + spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi.connect() + +ntp = NTP(esp) +while not ntp.valid_time: + ntp.set_time() + + if not ntp.valid_time: + time.sleep(1) # You will need an Azure subscription to create an Azure IoT Hub resource # @@ -46,13 +53,13 @@ # 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 DeviceConnectionString +# Add it to the secrets.py file in an entry called device_connection_string from adafruit_azureiot import IoTHubDevice from adafruit_azureiot.iot_mqtt import IoTResponse # Create an IoT Hub device client and connect -device = IoTHubDevice(wifi, secrets["DeviceConnectionString"]) +device = IoTHubDevice(wifi, 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, diff --git a/examples/iothub_messages.py b/examples/iothub_messages.py index 5ef27b1..ee1b26d 100644 --- a/examples/iothub_messages.py +++ b/examples/iothub_messages.py @@ -5,7 +5,7 @@ import busio from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel +from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file try: @@ -20,15 +20,22 @@ esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) + esp32_cs = DigitalInOut(board.D13) + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) + spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi.connect() + +ntp = NTP(esp) +while not ntp.valid_time: + ntp.set_time() + + if not ntp.valid_time: + time.sleep(1) # You will need an Azure subscription to create an Azure IoT Hub resource # @@ -48,12 +55,12 @@ # 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 DeviceConnectionString +# Add it to the secrets.py file in an entry called device_connection_string from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect -device = IoTHubDevice(wifi, secrets["DeviceConnectionString"]) +device = IoTHubDevice(wifi, 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, @@ -66,18 +73,18 @@ def cloud_to_device_message_received(body: str, properties: dict): device.connect() -message_counter = 0 +message_counter = 60 while True: # 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: + if message_counter >= 60: message = {"Temperature": random.randint(0, 50)} device.send_device_to_cloud_message(json.dumps(message)) message_counter = 0 - - message_counter = message_counter + 1 + else: + message_counter = message_counter + 1 # Poll every second for messages from the cloud device.loop() diff --git a/examples/iothub_simpletest.py b/examples/iothub_simpletest.py index 025281b..0c657ca 100644 --- a/examples/iothub_simpletest.py +++ b/examples/iothub_simpletest.py @@ -5,7 +5,7 @@ import busio from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel +from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file try: @@ -20,15 +20,22 @@ esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) + esp32_cs = DigitalInOut(board.D13) + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) + spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi.connect() + +ntp = NTP(esp) +while not ntp.valid_time: + ntp.set_time() + + if not ntp.valid_time: + time.sleep(1) # You will need an Azure subscription to create an Azure IoT Hub resource # @@ -48,26 +55,26 @@ # 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 DeviceConnectionString +# Add it to the secrets.py file in an entry called device_connection_string from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect -device = IoTHubDevice(wifi, secrets["DeviceConnectionString"]) +device = IoTHubDevice(wifi, secrets["device_connection_string"]) device.connect() -message_counter = 0 +message_counter = 60 while True: # 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: + if message_counter >= 60: message = {"Temperature": random.randint(0, 50)} device.send_device_to_cloud_message(json.dumps(message)) message_counter = 0 - - message_counter = message_counter + 1 + else: + message_counter = message_counter + 1 # Poll every second for messages from the cloud device.loop() diff --git a/examples/iothub_twin_operations.py b/examples/iothub_twin_operations.py index c4e528c..1f45341 100644 --- a/examples/iothub_twin_operations.py +++ b/examples/iothub_twin_operations.py @@ -4,7 +4,7 @@ import busio from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager -import neopixel +from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file try: @@ -19,15 +19,22 @@ esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) except AttributeError: - esp32_cs = DigitalInOut(board.D9) - esp32_ready = DigitalInOut(board.D10) - esp32_reset = DigitalInOut(board.D5) + esp32_cs = DigitalInOut(board.D13) + esp32_ready = DigitalInOut(board.D11) + esp32_reset = DigitalInOut(board.D12) + spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards -"""Uncomment below for ItsyBitsy M4""" -# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi.connect() + +ntp = NTP(esp) +while not ntp.valid_time: + ntp.set_time() + + if not ntp.valid_time: + time.sleep(1) # You will need an Azure subscription to create an Azure IoT Hub resource # @@ -47,12 +54,12 @@ # 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 DeviceConnectionString +# Add it to the secrets.py file in an entry called device_connection_string from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect -device = IoTHubDevice(wifi, secrets["DeviceConnectionString"]) +device = IoTHubDevice(wifi, 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 @@ -66,18 +73,18 @@ def device_twin_desired_updated(desired_property_name: str, desired_property_val device.connect() -message_counter = 0 +message_counter = 60 while True: - if message_counter > 60: + 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 - - message_counter = message_counter + 1 + else: + message_counter = message_counter + 1 # Poll every second for messages from the cloud device.loop() From 0d85cd2509b280e9d1ad1cb73f716727ba9b2ab0 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Wed, 15 Apr 2020 17:51:12 -0700 Subject: [PATCH 12/33] Fixing based of code reivew comments Also adding support for B tier hubs by lazy subscribing to twins --- adafruit_azureiot/device_registration.py | 12 +-- adafruit_azureiot/iot_mqtt.py | 67 ++++++++++------ adafruit_azureiot/iotcentral_device.py | 1 + adafruit_azureiot/iothub_device.py | 99 ++++++++++++++++++++---- examples/iothub_twin_operations.py | 3 + 5 files changed, 138 insertions(+), 44 deletions(-) diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index 2b3a560..d611dc9 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -88,11 +88,11 @@ def _loop_assign(self, operation_id, headers) -> str: self._logger.info("- iotc :: _loop_assign :: " + uri) target = parse.urlparse(uri) - response = self.__run_get_request_with_retry(target.geturl(), headers) + response = self._run_get_request_with_retry(target.geturl(), headers) try: data = response.json() - except Exception as error: + except ValueError as error: err = "ERROR: " + str(error) + " => " + str(response) self._logger.error(err) raise DeviceRegistrationError(err) @@ -120,7 +120,7 @@ def _loop_assign(self, operation_id, headers) -> str: self._logger.error(err) raise DeviceRegistrationError(err) - def __run_put_request_with_retry(self, url, body, headers): + def _run_put_request_with_retry(self, url, body, headers): retry = 0 response = None @@ -145,7 +145,7 @@ def __run_put_request_with_retry(self, url, body, headers): gc.collect() return response - def __run_get_request_with_retry(self, url, headers): + def _run_get_request_with_retry(self, url, headers): retry = 0 response = None @@ -206,12 +206,12 @@ def register_device(self, expiry: int) -> str: self._logger.info("body: " + json.dumps(body)) print("headers: " + json.dumps(headers)) - response = self.__run_put_request_with_retry(target.geturl(), body, headers) + response = self._run_put_request_with_retry(target.geturl(), body, headers) data = None try: data = response.json() - except Exception as e: + except ValueError as e: err = "ERROR: non JSON is received from " + self._dps_endpoint + " => " + str(response) + " .. message : " + str(e) self._logger.error(err) raise DeviceRegistrationError(err) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 896f855..9d4e7fe 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -13,24 +13,14 @@ from .device_registration import DeviceRegistration from . import constants - +# pylint: disable=R0903 class IoTResponse: """A response from a direct method call """ def __init__(self, code, message): - self._code = code - self._message = message - - def get_response_code(self): - """Gets the method response code - """ - return self._code - - def get_response_message(self): - """Gets the method response message - """ - return self._message + self.response_code = code + self.response_message = message class IoTMQTTCallback: @@ -162,7 +152,7 @@ def _handle_device_twin_update(self, msg: str, topic: str): try: twin = json.loads(msg) - except Exception as e: + except json.JSONDecodeError as e: self._logger.error("ERROR: JSON parse for Device Twin message object has failed. => " + msg + " => " + str(e)) return @@ -212,10 +202,10 @@ def _handle_direct_method(self, msg: str, topic: str): ret_code = 200 ret_message = "{}" - if ret.get_response_code() is not None: - ret_code = ret.get_response_code() - if ret.get_response_message() is not None: - ret_message = ret.get_response_message() + if ret.response_code() is not None: + ret_code = ret.response_code() + if ret.response_message() is not None: + ret_message = ret.response_message() # ret message must be JSON if not ret_message.startswith("{") or not ret_message.endswith("}"): @@ -279,6 +269,7 @@ def _send_common(self, topic, data) -> None: gc.collect() try: self._logger.debug("Trying to send...") + data = parse.urlencode(data) self._mqtts.publish(topic, data) self._logger.debug("Data sent") break @@ -333,6 +324,7 @@ def __init__( self._username = "{}/{}/api-version={}".format(self._hostname, device_id, self._iotc_api_version) self._passwd = self._gen_sas_token() self._logger = logger if logger is not None else logging.getLogger("log") + self._is_subscribed_to_twins = False def connect(self): """Connects to the MQTT broker @@ -354,16 +346,25 @@ def connect(self): self._mqtts.subscribe("devices/{}/messages/events/#".format(self._device_id)) self._mqtts.subscribe("devices/{}/messages/devicebound/#".format(self._device_id)) + self._mqtts.subscribe("$iothub/methods/#") + + return 0 + + def subscribe_to_twins(self): + """Subscribes to digital twin updates + Only call this if your tier of IoT Hub supports this + """ + if self._is_subscribed_to_twins: + return + + # do this separately as this is not supported in B1 hubs self._mqtts.subscribe("$iothub/twin/PATCH/properties/desired/#") # twin desired property changes self._mqtts.subscribe("$iothub/twin/res/#") # twin properties response - self._mqtts.subscribe("$iothub/methods/#") if self._get_device_settings() == 0: self._callback.settings_updated() - else: - return 1 - return 0 + self._is_subscribed_to_twins = True def disconnect(self): """Disconnects from the MQTT broker @@ -389,7 +390,27 @@ def loop(self): self._mqtts.loop() def _send_common(self, topic, data): - self._mqtts.publish(topic, data) + retry = 0 + + while True: + gc.collect() + try: + self._logger.debug("Trying to send message...") + self._mqtts.publish(topic, data) + self._logger.debug("Sent!") + break + except RuntimeError as runtime_error: + self._logger.info("Could not send message, retrying after 0.5 seconds: " + str(runtime_error)) + retry = retry + 1 + + if retry >= 10: + self._logger.error("Failed to send message") + raise + + time.sleep(0.5) + continue + + gc.collect() def send_device_to_cloud_message(self, data, system_properties=None) -> None: """Send a device to cloud message from this device to Azure IoT Hub diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index 733fff9..8e4d15d 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -75,6 +75,7 @@ def connect(self): self._mqtt = IoTMQTT(self, self._wifi_manager, hostname, self._device_id, self._key, self._token_expires, self._logger) self._mqtt.connect() + self._mqtt.subscribe_to_twins() def disconnect(self): """Disconnects from the MQTT broker diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index c1bc477..5353645 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -53,17 +53,17 @@ class IoTHubDevice(IoTMQTTCallback): def connection_status_change(self, connected: bool) -> None: """Called when the connection status changes """ - if self.on_connection_status_changed is not None: + if self._on_connection_status_changed is not None: # pylint: disable=E1102 - self.on_connection_status_changed(connected) + self._on_connection_status_changed(connected) # pylint: disable=W0613, R0201 def direct_method_called(self, method_name: str, payload) -> IoTResponse: """Called when a direct method is invoked """ - if self.on_direct_method_called is not None: + if self._on_direct_method_called is not None: # pylint: disable=E1102 - return self.on_direct_method_called(method_name, payload) + return self._on_direct_method_called(method_name, payload) raise IoTError("on_direct_method_called not set") @@ -71,23 +71,23 @@ def direct_method_called(self, method_name: str, payload) -> IoTResponse: def cloud_to_device_message_received(self, body: str, properties: dict): """Called when a cloud to device message is received """ - if self.on_cloud_to_device_message_received is not None: + if self._on_cloud_to_device_message_received is not None: # pylint: disable=E1102 - self.on_cloud_to_device_message_received(body, properties) + self._on_cloud_to_device_message_received(body, properties) def device_twin_desired_updated(self, desired_property_name: str, desired_property_value, desired_version: int) -> None: """Called when the device twin is updated """ - if self.on_device_twin_desired_updated is not None: + if self._on_device_twin_desired_updated is not None: # pylint: disable=E1102 - self.on_device_twin_desired_updated(desired_property_name, desired_property_value, desired_version) + self._on_device_twin_desired_updated(desired_property_name, desired_property_value, desired_version) def device_twin_reported_updated(self, reported_property_name: str, reported_property_value, reported_version: int) -> None: """Called when the device twin is updated """ - if self.on_device_twin_reported_updated is not None: + if self._on_device_twin_reported_updated is not None: # pylint: disable=E1102 - self.on_device_twin_reported_updated(reported_property_name, reported_property_value, reported_version) + self._on_device_twin_reported_updated(reported_property_name, reported_property_value, reported_version) def __init__(self, wifi_manager: ESPSPI_WiFiManager, device_connection_string: str, token_expires: int = 21600, logger: logging = None): self._wifi_manager = wifi_manager @@ -115,14 +115,80 @@ def __init__(self, wifi_manager: ESPSPI_WiFiManager, device_connection_string: s self._logger.debug("Device Id: " + self._device_id) self._logger.debug("Shared Access Key: " + self._shared_access_key) - self.on_connection_status_changed = None - self.on_direct_method_called = None - self.on_cloud_to_device_message_received = None - self.on_device_twin_desired_updated = None - self.on_device_twin_reported_updated = None + self._on_connection_status_changed = None + self._on_direct_method_called = None + self._on_cloud_to_device_message_received = None + self._on_device_twin_desired_updated = None + self._on_device_twin_reported_updated = None self._mqtt = None + @property + def on_connection_status_changed(self): + """Event fired when the connection status changes + """ + return self._on_connection_status_changed + + @on_connection_status_changed.setter + def on_connection_status_changed(self, new_on_connection_status_changed): + """Event fired when the connection status changes + """ + self._on_connection_status_changed = new_on_connection_status_changed + + @property + def on_direct_method_called(self): + """Event fired when a direct method is invoked + """ + return self._on_direct_method_called + + @on_direct_method_called.setter + def on_direct_method_called(self, new_on_direct_method_called): + """Event fired when a direct method is invoked + """ + self._on_direct_method_called = new_on_direct_method_called + + @property + def on_cloud_to_device_message_received(self): + """Event fired when a cloud to device message is received + """ + return self._on_cloud_to_device_message_received + + @on_cloud_to_device_message_received.setter + def on_cloud_to_device_message_received(self, new_on_cloud_to_device_message_received): + """Event fired when a cloud to device message is received + """ + self._on_cloud_to_device_message_received = new_on_cloud_to_device_message_received + + @property + def on_device_twin_desired_updated(self): + """Event fired when the desired properties on a device twin are updated + """ + return self._on_device_twin_desired_updated + + @on_device_twin_desired_updated.setter + def on_device_twin_desired_updated(self, new_on_device_twin_desired_updated): + """Event fired when the desired properties on a device twin are updated + """ + self._on_device_twin_desired_updated = new_on_device_twin_desired_updated + + if self._mqtt is not None: + self._mqtt.subscribe_to_twins() + + @property + def on_device_twin_reported_updated(self): + """Event fired when the reported properties on a device twin are updated + """ + return self._on_device_twin_reported_updated + + @on_device_twin_reported_updated.setter + def on_device_twin_reported_updated(self, new_on_device_twin_reported_updated): + """Event fired when the reported properties on a device twin are updated + """ + self._on_device_twin_reported_updated = new_on_device_twin_reported_updated + + if self._mqtt is not None: + self._mqtt.subscribe_to_twins() + def connect(self): """Connects to Azure IoT Central """ @@ -131,6 +197,9 @@ def connect(self): ) self._mqtt.connect() + if self._on_device_twin_desired_updated is not None or self._on_device_twin_reported_updated is not None: + self._mqtt.subscribe_to_twins() + def disconnect(self): """Disconnects from the MQTT broker """ diff --git a/examples/iothub_twin_operations.py b/examples/iothub_twin_operations.py index 1f45341..5b92335 100644 --- a/examples/iothub_twin_operations.py +++ b/examples/iothub_twin_operations.py @@ -55,6 +55,9 @@ # # 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 +# +# To us twins, you will need either a free or standard tier IoT Hub. Basic tier doesn't +# support twins from adafruit_azureiot import IoTHubDevice From bdece0b6c4bcdce030c45122c89ca18f3c6a7570 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Thu, 16 Apr 2020 14:13:07 -0700 Subject: [PATCH 13/33] Starting to add readme documentation --- README.rst | 96 ++++++++++++++++++++++++----- adafruit_azureiot/iot_mqtt.py | 4 +- adafruit_azureiot/iothub_device.py | 20 +++--- examples/iotcentral_commands.py | 5 +- examples/iotcentral_notconnected.py | 5 +- examples/iotcentral_properties.py | 5 +- examples/iotcentral_simpletest.py | 5 +- examples/iothub_directmethods.py | 9 ++- examples/iothub_messages.py | 5 +- examples/iothub_simpletest.py | 5 +- examples/iothub_twin_operations.py | 5 +- 11 files changed, 111 insertions(+), 53 deletions(-) diff --git a/README.rst b/README.rst index 3c77ee6..50c55d6 100644 --- a/README.rst +++ b/README.rst @@ -13,8 +13,7 @@ Adafruit_CircuitPython_AzureIoT :target: https://github.com/adafruit/Adafruit_CircuitPython_AzureIoT/actions/ :alt: Build Status -Access to `Microsoft Azure IoT `_ from a CircuitPython device. This library can perform device -messaging services (cloud-to-device, device-to-cloud), device services, and job services. +A CircuitPython device library for `Microsoft Azure IoT Services `_ from a CircuitPython device. This library only supports key-base authentication, it currently doesn't support X.509 certificates. Installing from PyPI ===================== @@ -45,49 +44,116 @@ Dependencies This driver depends on: * `Adafruit CircuitPython `_ +* `Adafruit CircuitPython ESP32 SPI `_ +* `Adafruit CircuitPython MiniMQTT `_ + +* `CircuitPython Base64 `_ +* `CircuitPython HMAC `_ +* `CircuitPython Parse `_ Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading -`the Adafruit library and driver bundle `_. +`the Adafruit library and driver bundle `_ +and +`the CircuitPython community library and driver bundle `_ Usage Example ============= -Create an instance of an Azure IoT Hub (you'll need your SAS Token). +This library supports both `Azure IoT Hub `_ and `Azure IoT Central `_. + +To create an Azure IoT Hub instance or an Azure IoT Central app, you will need an Azure subscription. If you don't have an Azure subscription, you can sign up for free: + +- If you are a student 18 or over, 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. You will not need a credit card. + +- 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. You will need a credit card for validation only, your card will not be charged. + +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 - my_hub = IOT_HUB(wifi, 'Azure_IOT_Hub_Name', 'Azure_IOT_Hub_SAS_Token', 'Azure_Device_Identifier') + ntp = NTP(esp) + + # Wait for a valid time to be received + while not ntp.valid_time: + time.sleep(0.1) + ntp.set_time() -Send a device-to-cloud message +Azure IoT Hub +------------- + +To interact with Azure IoT Hub, you will need to create a hub, and a register a device inside that hub. There is a free tier available, and this free tier allows up to 8,000 messages a day, so try not to send messages too often if you are using this tier. + +- Open the `Azure Portal `_. +- Follow the instructions in `Microsoft Docs `_ to create an Azure IoT Hub and register a device. +- Copy the devices Primary Key, and add this to your ``secrets.py`` file. + +**Connect your device to Azure IoT Hub** .. code-block:: python - my_hub.send_device_message('Hello Azure IoT!') + device = IoTHubDevice(wifi, secrets["device_connection_string"]) + device.connect() -Enumerate all devices on an Azure IOT Hub +Once the device is connected, you will regularly need to run a ``loop`` to poll for messages from the cloud. .. code-block:: python - hub_devices = my_hub.get_devices() + while True: + device.loop() + time.sleep(1) -Get information about the current device on an Azure IoT Hub +**Send a device to cloud message** .. code-block:: python - device_info = my_hub.get_device() + message = {"Temperature": temp} + device.send_device_to_cloud_message(json.dumps(message)) -Get information about the current device's device twin +**Receive device to cloud messages** .. code-block:: python - twin_info = my_hub.get_device_twin() + def cloud_to_device_message_received(body: str, properties: dict): + print("Received message with body", body, "and properties", json.dumps(properties)) -Update the current device's device twin properties + # Subscribe to cloud to device messages + device.on_cloud_to_device_message_received = cloud_to_device_message_received + +**Receive direct methods** .. code-block:: python - my_hub.update_device_twin(device_properties) + def direct_method_invoked(method_name: str, payload) -> IoTResponse: + print("Received direct method", method_name, "with data", str(payload)) + return IoTResponse(200, "OK") + + # Subscribe to direct methods + device.on_direct_method_invoked = direct_method_invoked + +**Update reported properties on the device twin** + +*This is not supported on Basic tier IoT Hubs, only on the free and standard tiers.* + +.. code-block:: python + + patch = {"Temperature": temp} + device.update_twin(patch) + +**Subscribe to desired property changes on the device twin** + +*This is not supported on Basic tier IoT Hubs, only on the free and standard tiers.* + +.. code-block:: python + + 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 desired property changes + device.on_device_twin_desired_updated = device_twin_desired_updated + +Azure IoT Central +----------------- Contributing ============ diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 9d4e7fe..4a2185b 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -36,7 +36,7 @@ def connection_status_change(self, connected: bool) -> None: """ # pylint: disable=W0613, R0201 - def direct_method_called(self, method_name: str, payload) -> IoTResponse: + def direct_method_invoked(self, method_name: str, payload) -> IoTResponse: """Called when a direct method is invoked """ return IoTResponse("", "") @@ -198,7 +198,7 @@ def _handle_direct_method(self, msg: str, topic: str): len_temp = len(topic_template) method_name = topic[len_temp : topic.find("/", len_temp + 1)] - ret = self._callback.direct_method_called(method_name, msg) + ret = self._callback.direct_method_invoked(method_name, msg) ret_code = 200 ret_message = "{}" diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index 5353645..8fb8568 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -58,14 +58,14 @@ def connection_status_change(self, connected: bool) -> None: self._on_connection_status_changed(connected) # pylint: disable=W0613, R0201 - def direct_method_called(self, method_name: str, payload) -> IoTResponse: + def direct_method_invoked(self, method_name: str, payload) -> IoTResponse: """Called when a direct method is invoked """ - if self._on_direct_method_called is not None: + if self._on_direct_method_invoked is not None: # pylint: disable=E1102 - return self._on_direct_method_called(method_name, payload) + return self._on_direct_method_invoked(method_name, payload) - raise IoTError("on_direct_method_called not set") + raise IoTError("on_direct_method_invoked not set") # pylint: disable=C0103 def cloud_to_device_message_received(self, body: str, properties: dict): @@ -116,7 +116,7 @@ def __init__(self, wifi_manager: ESPSPI_WiFiManager, device_connection_string: s self._logger.debug("Shared Access Key: " + self._shared_access_key) self._on_connection_status_changed = None - self._on_direct_method_called = None + self._on_direct_method_invoked = None self._on_cloud_to_device_message_received = None self._on_device_twin_desired_updated = None self._on_device_twin_reported_updated = None @@ -136,16 +136,16 @@ def on_connection_status_changed(self, new_on_connection_status_changed): self._on_connection_status_changed = new_on_connection_status_changed @property - def on_direct_method_called(self): + def on_direct_method_invoked(self): """Event fired when a direct method is invoked """ - return self._on_direct_method_called + return self._on_direct_method_invoked - @on_direct_method_called.setter - def on_direct_method_called(self, new_on_direct_method_called): + @on_direct_method_invoked.setter + def on_direct_method_invoked(self, new_on_direct_method_invoked): """Event fired when a direct method is invoked """ - self._on_direct_method_called = new_on_direct_method_called + self._on_direct_method_invoked = new_on_direct_method_invoked @property def on_cloud_to_device_message_received(self): diff --git a/examples/iotcentral_commands.py b/examples/iotcentral_commands.py index f564ff5..b964b9f 100644 --- a/examples/iotcentral_commands.py +++ b/examples/iotcentral_commands.py @@ -29,12 +29,11 @@ wifi.connect() ntp = NTP(esp) +# Wait for a valid time to be received while not ntp.valid_time: + time.sleep(0.1) ntp.set_time() - if not ntp.valid_time: - time.sleep(1) - # 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. diff --git a/examples/iotcentral_notconnected.py b/examples/iotcentral_notconnected.py index 04fad30..7556af2 100644 --- a/examples/iotcentral_notconnected.py +++ b/examples/iotcentral_notconnected.py @@ -31,12 +31,11 @@ wifi.connect() ntp = NTP(esp) +# Wait for a valid time to be received while not ntp.valid_time: + time.sleep(0.1) ntp.set_time() - if not ntp.valid_time: - time.sleep(1) - # 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. diff --git a/examples/iotcentral_properties.py b/examples/iotcentral_properties.py index 11014b4..0e73f3a 100644 --- a/examples/iotcentral_properties.py +++ b/examples/iotcentral_properties.py @@ -30,12 +30,11 @@ wifi.connect() ntp = NTP(esp) +# Wait for a valid time to be received while not ntp.valid_time: + time.sleep(0.1) ntp.set_time() - if not ntp.valid_time: - time.sleep(1) - # 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. diff --git a/examples/iotcentral_simpletest.py b/examples/iotcentral_simpletest.py index 6cfa122..2a60ec8 100644 --- a/examples/iotcentral_simpletest.py +++ b/examples/iotcentral_simpletest.py @@ -31,12 +31,11 @@ wifi.connect() ntp = NTP(esp) +# Wait for a valid time to be received while not ntp.valid_time: + time.sleep(0.1) ntp.set_time() - if not ntp.valid_time: - time.sleep(1) - # 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. diff --git a/examples/iothub_directmethods.py b/examples/iothub_directmethods.py index ec0a4f3..5c1dbbd 100644 --- a/examples/iothub_directmethods.py +++ b/examples/iothub_directmethods.py @@ -29,12 +29,11 @@ wifi.connect() ntp = NTP(esp) +# Wait for a valid time to be received while not ntp.valid_time: + time.sleep(0.1) ntp.set_time() - if not ntp.valid_time: - time.sleep(1) - # You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: @@ -66,12 +65,12 @@ # 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_called(method_name: str, payload) -> IoTResponse: +def direct_method_invoked(method_name: str, payload) -> IoTResponse: print("Received direct method", method_name, "with data", str(payload)) return IoTResponse(200, "OK") -device.on_direct_method_called = direct_method_called +device.on_direct_method_invoked = direct_method_invoked device.connect() diff --git a/examples/iothub_messages.py b/examples/iothub_messages.py index ee1b26d..dbfe878 100644 --- a/examples/iothub_messages.py +++ b/examples/iothub_messages.py @@ -31,12 +31,11 @@ wifi.connect() ntp = NTP(esp) +# Wait for a valid time to be received while not ntp.valid_time: + time.sleep(0.1) ntp.set_time() - if not ntp.valid_time: - time.sleep(1) - # You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: diff --git a/examples/iothub_simpletest.py b/examples/iothub_simpletest.py index 0c657ca..610c345 100644 --- a/examples/iothub_simpletest.py +++ b/examples/iothub_simpletest.py @@ -31,12 +31,11 @@ wifi.connect() ntp = NTP(esp) +# Wait for a valid time to be received while not ntp.valid_time: + time.sleep(0.1) ntp.set_time() - if not ntp.valid_time: - time.sleep(1) - # You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: diff --git a/examples/iothub_twin_operations.py b/examples/iothub_twin_operations.py index 5b92335..0bb497d 100644 --- a/examples/iothub_twin_operations.py +++ b/examples/iothub_twin_operations.py @@ -30,12 +30,11 @@ wifi.connect() ntp = NTP(esp) +# Wait for a valid time to be received while not ntp.valid_time: + time.sleep(0.1) ntp.set_time() - if not ntp.valid_time: - time.sleep(1) - # You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: From 79aab5a49e526d16a06c433f0a8ec105fdb31606 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Thu, 16 Apr 2020 14:34:28 -0700 Subject: [PATCH 14/33] More readme updates --- README.rst | 72 ++++++++++++++++++++++++++++++- examples/iotcentral_commands.py | 1 + examples/iotcentral_properties.py | 2 +- examples/iothub_directmethods.py | 1 + 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 50c55d6..2ab4aff 100644 --- a/README.rst +++ b/README.rst @@ -92,6 +92,8 @@ To interact with Azure IoT Hub, you will need to create a hub, and a register a .. code-block:: python + from adafruit_azureiot import IoTHubDevice + device = IoTHubDevice(wifi, secrets["device_connection_string"]) device.connect() @@ -126,6 +128,7 @@ Once the device is connected, you will regularly need to run a ``loop`` to poll 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 direct methods @@ -149,12 +152,79 @@ Once the device is connected, you will regularly need to run a ``loop`` to poll 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 desired property changes + # Subscribe to desired property changes device.on_device_twin_desired_updated = device_twin_desired_updated Azure IoT Central ----------------- +To use Azure IoT Central, you will need to create an Azure IoT Central app, create a device template and register a device against the template. + +- 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 Primary Key in your ``secrets.py`` file. + +**Connect your device to your Azure IoT Central app** + +.. code-block:: python + + from adafruit_azureiot import IoTCentralDevice + + device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"]) + device.connect() + +Once the device is connected, you will regularly need to run a ``loop`` to poll for messages from the cloud. + +.. code-block:: python + + while True: + device.loop() + time.sleep(1) + +**Send telemetry** + +.. code-block:: python + + message = {"Temperature": temp} + device.send_telemetry(json.dumps(message)) + +**Listen for commands** + +.. code-block:: python + + 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 commands + device.on_command_executed = command_executed + +**Update properties** + +.. code-block:: python + + device.send_property("Desired_Temperature", temp) + +**Listen for property updates** + +.. code-block:: python + + def property_changed(property_name, property_value, version): + print("Property", property_name, "updated to", str(property_value), "version", str(version)) + + # Subscribe to property updates + device.on_property_changed = property_changed + +Learning more about Azure IoT services +-------------------------------------- + +If you want to learn more about setting up or using Azure IoT Services, check out the following resources: + +-` Azure IoT documentation on Microsoft Docs `_ +- `IoT learning paths and modules on Microsoft Learn `_ - Free, online, self-guided hands on learning with Azure IoT services + Contributing ============ diff --git a/examples/iotcentral_commands.py b/examples/iotcentral_commands.py index b964b9f..ad175f1 100644 --- a/examples/iotcentral_commands.py +++ b/examples/iotcentral_commands.py @@ -72,6 +72,7 @@ # 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") diff --git a/examples/iotcentral_properties.py b/examples/iotcentral_properties.py index 0e73f3a..d2bd1b3 100644 --- a/examples/iotcentral_properties.py +++ b/examples/iotcentral_properties.py @@ -82,7 +82,7 @@ def property_changed(property_name, property_value, version): # Send property values every minute # You can see the values in the devices dashboard if message_counter >= 60: - device.send_property("Temperature", random.randint(0, 50)) + device.send_property("Desired_Temperature", random.randint(0, 50)) message_counter = 0 else: message_counter = message_counter + 1 diff --git a/examples/iothub_directmethods.py b/examples/iothub_directmethods.py index 5c1dbbd..29f71ae 100644 --- a/examples/iothub_directmethods.py +++ b/examples/iothub_directmethods.py @@ -67,6 +67,7 @@ # 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") From e86e9b365c76b318816883ef67784d286968b9c2 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Thu, 16 Apr 2020 14:36:45 -0700 Subject: [PATCH 15/33] Update README.rst --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 2ab4aff..bf0da16 100644 --- a/README.rst +++ b/README.rst @@ -64,9 +64,9 @@ This library supports both `Azure IoT Hub `_ 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. You will not need a credit card. -- 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. You will need a credit card for validation only, your card will not be charged. +- 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. 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: @@ -222,7 +222,7 @@ Learning more about Azure IoT services If you want to learn more about setting up or using Azure IoT Services, check out the following resources: --` Azure IoT documentation on Microsoft Docs `_ +- `Azure IoT documentation on Microsoft Docs `_ - `IoT learning paths and modules on Microsoft Learn `_ - Free, online, self-guided hands on learning with Azure IoT services Contributing From 6c04a3e53554dc78f963cbec42be87c620f79686 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Thu, 16 Apr 2020 14:39:30 -0700 Subject: [PATCH 16/33] Update README.rst --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index bf0da16..f2dafb8 100644 --- a/README.rst +++ b/README.rst @@ -60,7 +60,7 @@ and Usage Example ============= -This library supports both `Azure IoT Hub `_ and `Azure IoT Central `_. +This library supports both `Azure IoT Hub `_ and `Azure IoT Central `__. To create an Azure IoT Hub instance or an Azure IoT Central app, you will need an Azure subscription. If you don't have an Azure subscription, you can sign up for free: @@ -160,9 +160,9 @@ Azure IoT Central To use Azure IoT Central, you will need to create an Azure IoT Central app, create a device template and register a device against the template. -- 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. +- 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 Primary Key in your ``secrets.py`` file. **Connect your device to your Azure IoT Central app** From 0a043d43f70dc0265ea73bf69bcdb0a16e1dee3a Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Thu, 16 Apr 2020 17:33:26 -0700 Subject: [PATCH 17/33] Updating API docs --- adafruit_azureiot/constants.py | 7 +- adafruit_azureiot/device_registration.py | 28 +++-- adafruit_azureiot/iot_error.py | 5 +- adafruit_azureiot/iot_mqtt.py | 142 +++++++++++++---------- adafruit_azureiot/iotcentral_device.py | 65 +++++++++-- adafruit_azureiot/iothub_device.py | 89 ++++++++++---- 6 files changed, 233 insertions(+), 103 deletions(-) diff --git a/adafruit_azureiot/constants.py b/adafruit_azureiot/constants.py index cc7330b..ceffaad 100644 --- a/adafruit_azureiot/constants.py +++ b/adafruit_azureiot/constants.py @@ -1,6 +1,11 @@ """This file is for maintaining constants that could be changed or added to over time for different scenarios """ -DPS_API_VERSION = "2018-11-01" +# The version of the IoT Central MQTT API this code is built against IOTC_API_VERSION = "2016-11-14" + +# The version of the Azure Device Provisioning Service this code is built against +DPS_API_VERSION = "2018-11-01" + +# The Azure Device Provisioning service endpoint that this library uses to provision IoT Central devices DPS_END_POINT = "global.azure-devices-provisioning.net" diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index d611dc9..fc01a3b 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -18,8 +18,8 @@ import adafruit_hashlib as hashlib from . import constants - -AZURE_HTTP_ERROR_CODES = [400, 401, 404, 403, 412, 429, 500] # Azure HTTP Status Codes +# Azure HTTP error status codes +AZURE_HTTP_ERROR_CODES = [400, 401, 404, 403, 412, 429, 500] class DeviceRegistrationError(Exception): @@ -43,22 +43,24 @@ class DeviceRegistration: _loop_interval = 2 @staticmethod - def _parse_http_status(status_code, status_reason): + def _parse_http_status(status_code: int, status_reason: str) -> None: """Parses status code, throws error based on Azure IoT Common Error Codes. :param int status_code: HTTP status code. :param str status_reason: Description of HTTP status. + :raises DeviceRegistrationError: if the status code is an error code """ for error in AZURE_HTTP_ERROR_CODES: if error == status_code: - raise TypeError("Error {0}: {1}".format(status_code, status_reason)) + raise DeviceRegistrationError("Error {0}: {1}".format(status_code, status_reason)) def __init__(self, wifi_manager: ESPSPI_WiFiManager, id_scope: str, device_id: str, key: str, logger: Logger = None): - """Creates an instance of the device registration + """Creates an instance of the device registration service :param wifi_manager: WiFiManager object from ESPSPI_WiFiManager. :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 adafruit_logging.Logger key: The primary or secondary key of the device to register + :param adafruit_logging.Logger logger: The logger to use to log messages + :raises TypeError: if the WiFi manager is not the right type """ wifi_type = str(type(wifi_manager)) if "ESPSPI_WiFiManager" not in wifi_type: @@ -71,11 +73,15 @@ def __init__(self, wifi_manager: ESPSPI_WiFiManager, id_scope: str, device_id: s self._logger = logger if logger is not None else logging.getLogger("log") @staticmethod - def compute_derived_symmetric_key(secret, reg_id): + 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 + :rtype: bytes """ secret = base64.b64decode(secret) - return base64.b64encode(hmac.new(secret, msg=reg_id.encode("utf8"), digestmod=hashlib.sha256).digest()) + return base64.b64encode(hmac.new(secret, msg=msg.encode("utf8"), digestmod=hashlib.sha256).digest()) def _loop_assign(self, operation_id, headers) -> str: uri = "https://%s/%s/registrations/%s/operations/%s?api-version=%s" % ( @@ -174,7 +180,11 @@ 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 str expiry: The expiry time + :param int expiry: The expiry time for the registration + :returns: The underlying IoT Hub that this device should connect to + :rtype: str + :raises DeviceRegistrationError: if the device cannot be registered successfully + :raises RuntimeError: if the internet connection is not responding or is unable to connect """ # pylint: disable=c0103 sr = self._id_scope + "%2Fregistrations%2F" + self._device_id diff --git a/adafruit_azureiot/iot_error.py b/adafruit_azureiot/iot_error.py index 3b1cd9d..012e8f0 100644 --- a/adafruit_azureiot/iot_error.py +++ b/adafruit_azureiot/iot_error.py @@ -8,6 +8,9 @@ class IoTError(Exception): An error from the IoT service """ - def __init__(self, message): + def __init__(self, message: str): + """Create the IoT Error + :param str message: The error message + """ super(IoTError, self).__init__(message) self.message = message diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 4a2185b..7f8cea7 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -11,6 +11,7 @@ import circuitpython_parse as parse import adafruit_logging as logging from .device_registration import DeviceRegistration +from .iot_error import IoTError from . import constants # pylint: disable=R0903 @@ -18,7 +19,11 @@ class IoTResponse: """A response from a direct method call """ - def __init__(self, code, message): + 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 + """ self.response_code = code self.response_message = message @@ -27,35 +32,45 @@ class IoTMQTTCallback: """An interface for classes that can be called by MQTT events """ - def message_sent(self, data) -> None: + 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) -> IoTResponse: + 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 + :rtype: IoTResponse """ - return IoTResponse("", "") + return IoTResponse(200, "") # 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 """ 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 """ def device_twin_reported_updated(self, reported_property_name: str, reported_property_value, reported_version: int) -> None: """Called when the device twin reported values are updated - """ - - def settings_updated(self) -> None: - """Called when the settings 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 """ @@ -66,7 +81,7 @@ class IoTMQTT: _iotc_api_version = constants.IOTC_API_VERSION - def _gen_sas_token(self): + 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 = DeviceRegistration.compute_derived_symmetric_key(self._key, uri + "\n" + str(token_expiry)) @@ -77,7 +92,7 @@ def _gen_sas_token(self): return token # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/25 - def _try_create_mqtt_client(self, hostname): + def _try_create_mqtt_client(self, hostname: str) -> None: minimqtt.set_socket(socket, self._wifi_manager.esp) self._mqtts = MQTT( @@ -104,7 +119,7 @@ def _try_create_mqtt_client(self, hostname): self._mqtts.last_will() self._mqtts.connect() - def _create_mqtt_client(self): + def _create_mqtt_client(self) -> None: try: self._try_create_mqtt_client(self._hostname) except ValueError: @@ -112,7 +127,7 @@ def _create_mqtt_client(self): self._try_create_mqtt_client("https://" + self._hostname) # pylint: disable=C0103, W0613 - def _on_connect(self, client, userdata, _, rc): + def _on_connect(self, client, userdata, _, rc) -> None: self._logger.info("- iot_mqtt :: _on_connect :: rc = " + str(rc) + ", userdata = " + str(userdata)) if rc == 0: self._mqtt_connected = True @@ -120,12 +135,12 @@ def _on_connect(self, client, userdata, _, rc): self._callback.connection_status_change(True) # pylint: disable=C0103, W0613 - def _on_log(self, client, userdata, level, buf): + def _on_log(self, client, userdata, level, buf) -> None: self._logger.info("mqtt-log : " + buf) if level <= 8: self._logger.error("mqtt-log : " + buf) - def _on_disconnect(self, client, userdata, rc): + def _on_disconnect(self, client, userdata, rc) -> None: self._logger.info("- iot_mqtt :: _on_disconnect :: rc = " + str(rc)) self._auth_response_received = True @@ -139,11 +154,11 @@ def _on_disconnect(self, client, userdata, rc): if rc != 5: self._callback.connection_status_change(False) - def _on_publish(self, client, data, topic, msg_id): + def _on_publish(self, client, data, topic, msg_id) -> None: self._logger.info("- iot_mqtt :: _on_publish :: " + str(data) + " on topic " + str(topic)) # pylint: disable=W0703 - def _handle_device_twin_update(self, msg: str, topic: str): + def _handle_device_twin_update(self, msg: str, topic: str) -> None: self._logger.debug("- iot_mqtt :: _echo_desired :: " + topic) twin = None desired = None @@ -186,7 +201,7 @@ def _handle_device_twin_update(self, msg: str, topic: str): for property_name, value in desired.items(): self._callback.device_twin_desired_updated(property_name, value, desired_version) - def _handle_direct_method(self, msg: str, topic: str): + def _handle_direct_method(self, msg: str, topic: str) -> None: index = topic.find("$rid=") method_id = 1 method_name = "None" @@ -202,10 +217,10 @@ def _handle_direct_method(self, msg: str, topic: str): ret_code = 200 ret_message = "{}" - if ret.response_code() is not None: - ret_code = ret.response_code() - if ret.response_message() is not None: - ret_message = ret.response_message() + if ret.response_code is not None: + ret_code = ret.response_code + if ret.response_message is not None: + ret_message = ret.response_message # ret message must be JSON if not ret_message.startswith("{") or not ret_message.endswith("}"): @@ -216,7 +231,7 @@ def _handle_direct_method(self, msg: str, topic: str): self._logger.info("C2D: => " + next_topic + " with data " + ret_message + " and name => " + method_name) self._send_common(next_topic, ret_message) - def _handle_cloud_to_device_message(self, msg: str, topic: str): + def _handle_cloud_to_device_message(self, msg: str, topic: str) -> None: parts = topic.split("&")[1:] properties = {} @@ -227,7 +242,7 @@ def _handle_cloud_to_device_message(self, msg: str, topic: str): self._callback.cloud_to_device_message_received(msg, properties) # pylint: disable=W0702, R0912 - def _on_message(self, client, msg_topic, payload): + def _on_message(self, client, msg_topic, payload) -> None: topic = "" msg = None @@ -259,7 +274,14 @@ def _on_message(self, client, msg_topic, payload): else: self._logger.error("ERROR: (unknown message) - {}".format(msg)) - def _send_common(self, topic, data) -> None: + def _send_common(self, topic: str, data) -> None: + # Convert data to a string + if isinstance(data, dict): + data = json.dumps(data) + + if not isinstance(data, str): + raise IoTError("Data must be a string or a dictionary") + self._logger.debug("Sending message on topic: " + topic) self._logger.debug("Sending message: " + str(data)) @@ -326,8 +348,10 @@ def __init__( self._logger = logger if logger is not None else logging.getLogger("log") self._is_subscribed_to_twins = False - def connect(self): + def connect(self) -> bool: """Connects to the MQTT broker + :returns: True if the connection is successful, otherwise False + :rtype: bool """ self._logger.info("- iot_mqtt :: connect :: " + self._hostname) @@ -339,7 +363,7 @@ def connect(self): self._logger.info(" - iot_mqtt :: connect :: on_connect must be fired. Connected ? " + str(self.is_connected())) if not self.is_connected(): - return 1 + return False self._mqtt_connected = True self._auth_response_received = True @@ -348,9 +372,9 @@ def connect(self): self._mqtts.subscribe("devices/{}/messages/devicebound/#".format(self._device_id)) self._mqtts.subscribe("$iothub/methods/#") - return 0 + return True - def subscribe_to_twins(self): + def subscribe_to_twins(self) -> None: """Subscribes to digital twin updates Only call this if your tier of IoT Hub supports this """ @@ -361,12 +385,11 @@ def subscribe_to_twins(self): self._mqtts.subscribe("$iothub/twin/PATCH/properties/desired/#") # twin desired property changes self._mqtts.subscribe("$iothub/twin/res/#") # twin properties response - if self._get_device_settings() == 0: - self._callback.settings_updated() + self._get_device_settings() self._is_subscribed_to_twins = True - def disconnect(self): + def disconnect(self) -> None: """Disconnects from the MQTT broker """ if not self.is_connected(): @@ -376,12 +399,14 @@ def disconnect(self): self._mqtt_connected = False self._mqtts.disconnect() - def is_connected(self): + 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 """ return self._mqtt_connected - def loop(self): + def loop(self) -> None: """Listens for MQTT messages """ if not self.is_connected(): @@ -389,33 +414,14 @@ def loop(self): self._mqtts.loop() - def _send_common(self, topic, data): - retry = 0 - - while True: - gc.collect() - try: - self._logger.debug("Trying to send message...") - self._mqtts.publish(topic, data) - self._logger.debug("Sent!") - break - except RuntimeError as runtime_error: - self._logger.info("Could not send message, retrying after 0.5 seconds: " + str(runtime_error)) - retry = retry + 1 - - if retry >= 10: - self._logger.error("Failed to send message") - raise - - time.sleep(0.5) - continue - - gc.collect() - - def send_device_to_cloud_message(self, data, system_properties=None) -> None: + 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 + :raises RuntimeError: if the internet connection is not responding or is unable to connect """ - self._logger.info("- iot_mqtt :: send_device_to_cloud_message :: " + data) + self._logger.info("- iot_mqtt :: send_device_to_cloud_message :: " + message) topic = "devices/{}/messages/events/".format(self._device_id) if system_properties is not None: @@ -427,12 +433,22 @@ def send_device_to_cloud_message(self, data, system_properties=None) -> None: firstProp = False topic += prop + "=" + str(system_properties[prop]) - self._send_common(topic, data) - self._callback.message_sent(data) + # Convert message to a string + if isinstance(message, dict): + message = json.dumps(message) + + if not isinstance(message, str): + raise ValueError("message must be a string or a dictionary") + + self._send_common(topic, message) + self._callback.message_sent(message) - def send_twin_patch(self, data): + 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 """ - self._logger.info("- iot_mqtt :: sendProperty :: " + data) + self._logger.info("- iot_mqtt :: sendProperty :: " + str(patch)) topic = "$iothub/twin/PATCH/properties/reported/?$rid={}".format(int(time.time())) - return self._send_common(topic, data) + self._send_common(topic, patch) diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index 8e4d15d..8427bef 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -16,14 +16,19 @@ 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: # pylint: disable=E1102 self.on_connection_status_changed(connected) # pylint: disable=W0613, R0201 - def direct_method_called(self, method_name: str, payload) -> IoTResponse: + 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 + :rtype: IoTResponse """ if self.on_command_executed is not None: # pylint: disable=E1102 @@ -32,7 +37,10 @@ def direct_method_called(self, method_name: str, payload) -> IoTResponse: raise IoTError("on_command_executed not set") def device_twin_desired_updated(self, desired_property_name: str, desired_property_value, desired_version: int) -> None: - """Called when the device twin is updated + """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 """ if self.on_property_changed is not None: # pylint: disable=E1102 @@ -42,7 +50,10 @@ def device_twin_desired_updated(self, desired_property_name: str, desired_proper self.send_property(desired_property_name, desired_property_value) def device_twin_reported_updated(self, reported_property_name: str, reported_property_value, reported_version: int) -> None: - """Called when the device twin is updated + """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 """ if self.on_property_changed is not None: # pylint: disable=E1102 @@ -52,6 +63,14 @@ def device_twin_reported_updated(self, reported_property_name: str, reported_pro def __init__( self, wifi_manager: ESPSPI_WiFiManager, id_scope: str, device_id: str, key: str, token_expires: int = 21600, logger: logging = None ): + """Create the Azure IoT Central device client + :param wifi_manager: The WiFi manager + :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 int token_expires: The number of seconds till the token expires, defaults to 6 hours + :param adafruit_logging logger: The logger + """ self._wifi_manager = wifi_manager self._id_scope = id_scope self._device_id = device_id @@ -62,11 +81,30 @@ def __init__( self._mqtt = None self.on_connection_status_changed = None + """A callback method that is called when the connection status is changed. This method should have the following signature: + def connection_status_changed(connected: bool) -> None + """ + self.on_command_executed = None + """A callback method that is called when a command is executed on the device. This method should have the following signature: + def connection_status_changed(method_name: str, payload: str) -> IoTResponse: + + This method returns an IoTResponse containing a status code and message from the command call. Set this appropriately + depending on if the command was successfully handled or not. For example, if the command was handled successfully, set + the code to 200 and message to "OK": + + return IoTResponse(200, "OK") + """ + self.on_property_changed = None + """A callback method that is called when property values are updated. This method should have the following signature: + def property_changed(_property_name: str, property_value, version: int) -> None + """ - def connect(self): + 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 """ self._device_registration = DeviceRegistration(self._wifi_manager, self._id_scope, self._device_id, self._key, self._logger) @@ -77,8 +115,9 @@ def connect(self): self._mqtt.connect() self._mqtt.subscribe_to_twins() - def disconnect(self): + 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: raise IoTError("You are not connected to IoT Central") @@ -87,32 +126,40 @@ def disconnect(self): 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 """ if self._mqtt is not None: return self._mqtt.is_connected() return False - def loop(self): + 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: raise IoTError("You are not connected to IoT Central") self._mqtt.loop() - def send_property(self, property_name, data): + 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 """ if self._mqtt is None: raise IoTError("You are not connected to IoT Central") - patch_json = {property_name: data} + patch_json = {property_name: value} patch = json.dumps(patch_json) self._mqtt.send_twin_patch(patch) - def send_telemetry(self, data): + 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 """ if self._mqtt is None: raise IoTError("You are not connected to IoT Central") diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index 8fb8568..72726ab 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -52,6 +52,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: # pylint: disable=E1102 @@ -60,6 +61,10 @@ def connection_status_change(self, connected: bool) -> None: # pylint: disable=W0613, R0201 def direct_method_invoked(self, method_name: str, payload) -> 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 + :rtype: IoTResponse """ if self._on_direct_method_invoked is not None: # pylint: disable=E1102 @@ -68,22 +73,30 @@ def direct_method_invoked(self, method_name: str, payload) -> IoTResponse: raise IoTError("on_direct_method_invoked not set") # pylint: disable=C0103 - def cloud_to_device_message_received(self, body: str, properties: dict): + 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 """ if self._on_cloud_to_device_message_received is not None: # pylint: disable=E1102 self._on_cloud_to_device_message_received(body, properties) def device_twin_desired_updated(self, desired_property_name: str, desired_property_value, desired_version: int) -> None: - """Called when the device twin is updated + """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 """ if self._on_device_twin_desired_updated is not None: # pylint: disable=E1102 self._on_device_twin_desired_updated(desired_property_name, desired_property_value, desired_version) def device_twin_reported_updated(self, reported_property_name: str, reported_property_value, reported_version: int) -> None: - """Called when the device twin is updated + """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 """ if self._on_device_twin_reported_updated is not None: # pylint: disable=E1102 @@ -125,49 +138,71 @@ def __init__(self, wifi_manager: ESPSPI_WiFiManager, device_connection_string: s @property def on_connection_status_changed(self): - """Event fired when the connection status changes + """A callback method that is called when the connection status is changed. This method should have the following signature: + def connection_status_changed(connected: bool) -> None """ return self._on_connection_status_changed @on_connection_status_changed.setter def on_connection_status_changed(self, new_on_connection_status_changed): - """Event fired when the connection status changes + """A callback method that is called when the connection status is changed. This method should have the following signature: + def connection_status_changed(connected: bool) -> None """ self._on_connection_status_changed = new_on_connection_status_changed @property def on_direct_method_invoked(self): - """Event fired when a direct method is invoked + """A callback method that is called when a direct method is invoked. This method should have the following signature: + def direct_method_invoked(method_name: str, payload: str) -> IoTResponse: + + This method returns an IoTResponse containing a status code and message from the method invocation. Set this appropriately + depending on if the method was successfully handled or not. For example, if the method was handled successfully, set + the code to 200 and message to "OK": + + return IoTResponse(200, "OK") """ return self._on_direct_method_invoked @on_direct_method_invoked.setter def on_direct_method_invoked(self, new_on_direct_method_invoked): - """Event fired when a direct method is invoked + """A callback method that is called when a direct method is invoked. This method should have the following signature: + def direct_method_invoked(method_name: str, payload: str) -> IoTResponse: + + This method returns an IoTResponse containing a status code and message from the method invocation. Set this appropriately + depending on if the method was successfully handled or not. For example, if the method was handled successfully, set + the code to 200 and message to "OK": + + return IoTResponse(200, "OK") """ self._on_direct_method_invoked = new_on_direct_method_invoked @property def on_cloud_to_device_message_received(self): - """Event fired when a cloud to device message is received + """A callback method that is called when a cloud to device message is received. This method should have the following signature: + def cloud_to_device_message_received(body: str, properties: dict) -> None: """ return self._on_cloud_to_device_message_received @on_cloud_to_device_message_received.setter def on_cloud_to_device_message_received(self, new_on_cloud_to_device_message_received): - """Event fired when a cloud to device message is received + """A callback method that is called when a cloud to device message is received. This method should have the following signature: + def cloud_to_device_message_received(body: str, properties: dict) -> None: """ self._on_cloud_to_device_message_received = new_on_cloud_to_device_message_received @property def on_device_twin_desired_updated(self): - """Event fired when the desired properties on a device twin are updated + """A callback method that is called when the desired properties of the devices device twin are updated. + This method should have the following signature: + def device_twin_desired_updated(desired_property_name: str, desired_property_value, desired_version: int) -> None: """ return self._on_device_twin_desired_updated @on_device_twin_desired_updated.setter def on_device_twin_desired_updated(self, new_on_device_twin_desired_updated): - """Event fired when the desired properties on a device twin are updated + """A callback method that is called when the desired properties of the devices device twin are updated. + This method should have the following signature: + def device_twin_desired_updated(desired_property_name: str, desired_property_value, desired_version: int) -> None: """ self._on_device_twin_desired_updated = new_on_device_twin_desired_updated @@ -176,21 +211,26 @@ def on_device_twin_desired_updated(self, new_on_device_twin_desired_updated): @property def on_device_twin_reported_updated(self): - """Event fired when the reported properties on a device twin are updated + """A callback method that is called when the reported properties of the devices device twin are updated. + This method should have the following signature: + def device_twin_reported_updated(reported_property_name: str, reported_property_value, reported_version: int) -> None: """ return self._on_device_twin_reported_updated @on_device_twin_reported_updated.setter def on_device_twin_reported_updated(self, new_on_device_twin_reported_updated): - """Event fired when the reported properties on a device twin are updated + """A callback method that is called when the reported properties of the devices device twin are updated. + This method should have the following signature: + def device_twin_reported_updated(reported_property_name: str, reported_property_value, reported_version: int) -> None: """ self._on_device_twin_reported_updated = new_on_device_twin_reported_updated if self._mqtt is not None: self._mqtt.subscribe_to_twins() - def connect(self): - """Connects to Azure IoT Central + 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( self, self._wifi_manager, self._hostname, self._device_id, self._shared_access_key, self._token_expires, self._logger @@ -200,8 +240,9 @@ def connect(self): if self._on_device_twin_desired_updated is not None or self._on_device_twin_reported_updated is not None: self._mqtt.subscribe_to_twins() - def disconnect(self): + 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: raise IoTError("You are not connected to IoT Central") @@ -210,30 +251,38 @@ def disconnect(self): 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 """ if self._mqtt is not None: return self._mqtt.is_connected() return False - def loop(self): + 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: raise IoTError("You are not connected to IoT Central") self._mqtt.loop() - def send_device_to_cloud_message(self, message, system_properties=None): - """Sends a device to cloud message to the IoT Hub + def send_device_to_cloud_message(self, message, system_properties=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 + :raises RuntimeError: if the internet connection is not responding or is unable to connect """ if self._mqtt is None: raise IoTError("You are not connected to IoT Central") self._mqtt.send_device_to_cloud_message(message, system_properties) - def update_twin(self, patch): + def update_twin(self, patch) -> 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: raise IoTError("You are not connected to IoT Central") From 45071bbe9a7164deeebfe15d91ea5e2670e93e83 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Thu, 16 Apr 2020 17:38:54 -0700 Subject: [PATCH 18/33] Setting the logging level for the MQTT client from the top level logger --- adafruit_azureiot/iot_mqtt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 7f8cea7..985bfef 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -106,7 +106,7 @@ def _try_create_mqtt_client(self, hostname: str) -> None: log=True, ) - self._mqtts.logger.setLevel(logging.INFO) + self._mqtts.logger.setLevel(self._logger.getEffectiveLevel()) # set actions to take throughout connection lifecycle self._mqtts.on_connect = self._on_connect From 0aa0e9ca4f5779b3cb0d9418b0eb9299586f386b Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Thu, 16 Apr 2020 17:44:09 -0700 Subject: [PATCH 19/33] Update iot_mqtt.py --- adafruit_azureiot/iot_mqtt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 985bfef..d46eedc 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -291,7 +291,6 @@ def _send_common(self, topic: str, data) -> None: gc.collect() try: self._logger.debug("Trying to send...") - data = parse.urlencode(data) self._mqtts.publish(topic, data) self._logger.debug("Data sent") break From e235d39c7af491d2034bb0bec011fa873e87afb6 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Fri, 17 Apr 2020 09:24:23 -0700 Subject: [PATCH 20/33] Added file headers --- adafruit_azureiot/constants.py | 29 +++++++++++++++++++++++- adafruit_azureiot/device_registration.py | 25 +++++++++++++++++++- adafruit_azureiot/iot_error.py | 26 +++++++++++++++++++++ adafruit_azureiot/iot_mqtt.py | 29 +++++++++++++++++++++++- adafruit_azureiot/iotcentral_device.py | 29 +++++++++++++++++++++++- adafruit_azureiot/iothub_device.py | 29 +++++++++++++++++++++++- 6 files changed, 162 insertions(+), 5 deletions(-) diff --git a/adafruit_azureiot/constants.py b/adafruit_azureiot/constants.py index ceffaad..d7db475 100644 --- a/adafruit_azureiot/constants.py +++ b/adafruit_azureiot/constants.py @@ -1,4 +1,31 @@ -"""This file is for maintaining constants that could be changed or added to over time for different scenarios +# The MIT License (MIT) +# +# Copyright (c) 2019 Jim Bennett +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`constants` +================================================================================ + +This file is for maintaining Microsoft Azure IoT constants that could be changed or added to over time for different scenarios + +* Author(s): Jim Bennett, Elena Horton """ # The version of the IoT Central MQTT API this code is built against diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index fc01a3b..2bb8e67 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -1,9 +1,32 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Jim Bennett +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. """ -Device Registration +`device_registration` ===================== Handles registration of IoT Central devices, and gets the hostname to use when connecting to IoT Central over MQTT + +* Author(s): Jim Bennett, Elena Horton """ import gc diff --git a/adafruit_azureiot/iot_error.py b/adafruit_azureiot/iot_error.py index 012e8f0..1f3631b 100644 --- a/adafruit_azureiot/iot_error.py +++ b/adafruit_azureiot/iot_error.py @@ -1,5 +1,31 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Jim Bennett +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. """ +`iot_error` +===================== + An error from the IoT service + +* Author(s): Jim Bennett, Elena Horton """ diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index d46eedc..28070f0 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -1,4 +1,31 @@ -"""MQTT client for Azure IoT +# The MIT License (MIT) +# +# Copyright (c) 2019 Jim Bennett +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`iot_mqtt` +===================== + +An MQTT client for Azure IoT + +* Author(s): Jim Bennett, Elena Horton """ import gc diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index 8427bef..acf0103 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -1,4 +1,31 @@ -"""Connectivity to Azure IoT Central +# The MIT License (MIT) +# +# Copyright (c) 2019 Jim Bennett +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`iotcentral_device` +===================== + +Connectivity to Azure IoT Central + +* Author(s): Jim Bennett, Elena Horton """ import json diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index 72726ab..db0cb78 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -1,4 +1,31 @@ -"""Connectivity to Azure IoT Hub +# The MIT License (MIT) +# +# Copyright (c) 2019 Jim Bennett +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`iothub_device` +===================== + +Connectivity to Azure IoT Hub + +* Author(s): Jim Bennett, Elena Horton """ import json From 0c034f478378e9c07ff9aee76b4153b2ce24032f Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Fri, 17 Apr 2020 09:26:54 -0700 Subject: [PATCH 21/33] Removing some rogue print statements --- adafruit_azureiot/device_registration.py | 1 - adafruit_azureiot/iot_mqtt.py | 4 ---- 2 files changed, 5 deletions(-) diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index 2bb8e67..ef041f0 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -237,7 +237,6 @@ def register_device(self, expiry: int) -> str: self._logger.info("Connecting...") self._logger.info("URL: " + target.geturl()) self._logger.info("body: " + json.dumps(body)) - print("headers: " + json.dumps(headers)) response = self._run_put_request_with_retry(target.geturl(), body, headers) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 28070f0..ca9ceca 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -190,8 +190,6 @@ def _handle_device_twin_update(self, msg: str, topic: str) -> None: twin = None desired = None - print(msg) - try: twin = json.loads(msg) except json.JSONDecodeError as e: @@ -273,7 +271,6 @@ def _on_message(self, client, msg_topic, payload) -> None: topic = "" msg = None - print("Topic: ", str(msg_topic)) self._logger.info("- iot_mqtt :: _on_message :: payload(" + str(payload) + ")") if payload is not None: @@ -332,7 +329,6 @@ def _send_common(self, topic: str, data) -> None: time.sleep(0.5) continue - print("finished _send_common") gc.collect() def _get_device_settings(self) -> None: From e8caff8f3639ad7b38bf05d69deda28bcb867b5a Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Fri, 17 Apr 2020 10:33:45 -0700 Subject: [PATCH 22/33] PR review changes --- README.rst | 2 +- adafruit_azureiot/device_registration.py | 12 +++++------- adafruit_azureiot/iot_mqtt.py | 4 +--- examples/iotcentral_commands.py | 2 +- examples/iotcentral_notconnected.py | 2 +- examples/iotcentral_properties.py | 2 +- examples/iotcentral_simpletest.py | 2 +- examples/iothub_directmethods.py | 2 +- examples/iothub_messages.py | 2 +- examples/iothub_simpletest.py | 2 +- examples/iothub_twin_operations.py | 2 +- 11 files changed, 15 insertions(+), 19 deletions(-) diff --git a/README.rst b/README.rst index f2dafb8..aa2a31d 100644 --- a/README.rst +++ b/README.rst @@ -76,7 +76,7 @@ To use this library, you will need to create an ESP32_SPI WifiManager, connected # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() Azure IoT Hub diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index ef041f0..3a016e0 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -61,8 +61,6 @@ class DeviceRegistration: to IoT Central over MQTT """ - _dps_endpoint = constants.DPS_END_POINT - _dps_api_version = constants.DPS_API_VERSION _loop_interval = 2 @staticmethod @@ -108,11 +106,11 @@ def compute_derived_symmetric_key(secret: str, msg: str) -> bytes: def _loop_assign(self, operation_id, headers) -> str: uri = "https://%s/%s/registrations/%s/operations/%s?api-version=%s" % ( - self._dps_endpoint, + constants.DPS_END_POINT, self._id_scope, self._device_id, operation_id, - self._dps_api_version, + constants.DPS_API_VERSION, ) self._logger.info("- iotc :: _loop_assign :: " + uri) target = parse.urlparse(uri) @@ -227,10 +225,10 @@ def register_device(self, expiry: int) -> str: body = {"registrationId": self._device_id} uri = "https://%s/%s/registrations/%s/register?api-version=%s" % ( - self._dps_endpoint, + constants.DPS_END_POINT, self._id_scope, self._device_id, - self._dps_api_version, + constants.DPS_API_VERSION, ) target = parse.urlparse(uri) @@ -244,7 +242,7 @@ def register_device(self, expiry: int) -> str: try: data = response.json() except ValueError as e: - err = "ERROR: non JSON is received from " + self._dps_endpoint + " => " + str(response) + " .. message : " + str(e) + err = "ERROR: non JSON is received from " + constants.DPS_END_POINT + " => " + str(response) + " .. message : " + str(e) self._logger.error(err) raise DeviceRegistrationError(err) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index ca9ceca..926f03c 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -106,8 +106,6 @@ class IoTMQTT: """MQTT client for Azure IoT """ - _iotc_api_version = constants.IOTC_API_VERSION - def _gen_sas_token(self) -> str: token_expiry = int(time.time() + self._token_expires) uri = self._hostname + "%2Fdevices%2F" + self._device_id @@ -365,7 +363,7 @@ def __init__( self._hostname = hostname self._key = key self._token_expires = token_expires - self._username = "{}/{}/api-version={}".format(self._hostname, device_id, self._iotc_api_version) + self._username = "{}/{}/api-version={}".format(self._hostname, device_id, constants.IOTC_API_VERSION) self._passwd = self._gen_sas_token() self._logger = logger if logger is not None else logging.getLogger("log") self._is_subscribed_to_twins = False diff --git a/examples/iotcentral_commands.py b/examples/iotcentral_commands.py index ad175f1..efe39dd 100644 --- a/examples/iotcentral_commands.py +++ b/examples/iotcentral_commands.py @@ -31,7 +31,7 @@ ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() # To use Azure IoT Central, you will need to create an IoT Central app. diff --git a/examples/iotcentral_notconnected.py b/examples/iotcentral_notconnected.py index 7556af2..190d6fc 100644 --- a/examples/iotcentral_notconnected.py +++ b/examples/iotcentral_notconnected.py @@ -33,7 +33,7 @@ ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() # To use Azure IoT Central, you will need to create an IoT Central app. diff --git a/examples/iotcentral_properties.py b/examples/iotcentral_properties.py index d2bd1b3..03d6f80 100644 --- a/examples/iotcentral_properties.py +++ b/examples/iotcentral_properties.py @@ -32,7 +32,7 @@ ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() # To use Azure IoT Central, you will need to create an IoT Central app. diff --git a/examples/iotcentral_simpletest.py b/examples/iotcentral_simpletest.py index 2a60ec8..66e55c2 100644 --- a/examples/iotcentral_simpletest.py +++ b/examples/iotcentral_simpletest.py @@ -33,7 +33,7 @@ ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() # To use Azure IoT Central, you will need to create an IoT Central app. diff --git a/examples/iothub_directmethods.py b/examples/iothub_directmethods.py index 29f71ae..c029e2e 100644 --- a/examples/iothub_directmethods.py +++ b/examples/iothub_directmethods.py @@ -31,7 +31,7 @@ ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() # You will need an Azure subscription to create an Azure IoT Hub resource diff --git a/examples/iothub_messages.py b/examples/iothub_messages.py index dbfe878..f5368f2 100644 --- a/examples/iothub_messages.py +++ b/examples/iothub_messages.py @@ -33,7 +33,7 @@ ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() # You will need an Azure subscription to create an Azure IoT Hub resource diff --git a/examples/iothub_simpletest.py b/examples/iothub_simpletest.py index 610c345..fc27a6d 100644 --- a/examples/iothub_simpletest.py +++ b/examples/iothub_simpletest.py @@ -33,7 +33,7 @@ ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() # You will need an Azure subscription to create an Azure IoT Hub resource diff --git a/examples/iothub_twin_operations.py b/examples/iothub_twin_operations.py index 0bb497d..4627616 100644 --- a/examples/iothub_twin_operations.py +++ b/examples/iothub_twin_operations.py @@ -32,7 +32,7 @@ ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: - time.sleep(0.1) + time.sleep(5) ntp.set_time() # You will need an Azure subscription to create an Azure IoT Hub resource From 5251721514de7b6d7d057b1f7be8b7df27835bee Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Fri, 17 Apr 2020 14:28:41 -0700 Subject: [PATCH 23/33] More updates from PR feedback --- .pylintrc | 2 +- LICENSE | 2 +- adafruit_azureiot/__init__.py | 2 +- adafruit_azureiot/constants.py | 2 +- adafruit_azureiot/device_registration.py | 20 +++---- adafruit_azureiot/iot_error.py | 2 +- adafruit_azureiot/iot_mqtt.py | 69 +++++++++++++++++++----- adafruit_azureiot/iotcentral_device.py | 27 ++++++---- adafruit_azureiot/iothub_device.py | 25 +++++++-- examples/iotcentral_commands.py | 44 +++++++++++++-- examples/iotcentral_notconnected.py | 26 ++++++++- examples/iotcentral_properties.py | 60 ++++++++++++++++----- examples/iotcentral_simpletest.py | 62 ++++++++++++++++----- examples/iothub_directmethods.py | 44 +++++++++++++-- examples/iothub_messages.py | 64 +++++++++++++++++----- examples/iothub_simpletest.py | 64 +++++++++++++++++----- examples/iothub_twin_operations.py | 64 +++++++++++++++++----- 17 files changed, 458 insertions(+), 121 deletions(-) diff --git a/.pylintrc b/.pylintrc index bef1f5a..7eb328d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -399,7 +399,7 @@ max-args=6 # Maximum number of attributes for a class (see R0902). # max-attributes=7 -max-attributes=12 +max-attributes=13 # Maximum number of boolean expressions in a if statement max-bool-expr=5 diff --git a/LICENSE b/LICENSE index d4fbf1d..f5d21b6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019 Brent Rubell for Adafruit Industries +Copyright (c) 2020 Brent Rubell for Adafruit Industries, Jim Bennett, Elena Horton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/adafruit_azureiot/__init__.py b/adafruit_azureiot/__init__.py index d631969..5f7f04a 100644 --- a/adafruit_azureiot/__init__.py +++ b/adafruit_azureiot/__init__.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2019 Jim Bennett +# Copyright (c) 2020 Jim Bennett # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/adafruit_azureiot/constants.py b/adafruit_azureiot/constants.py index d7db475..5770342 100644 --- a/adafruit_azureiot/constants.py +++ b/adafruit_azureiot/constants.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2019 Jim Bennett +# Copyright (c) 2020 Jim Bennett # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index 3a016e0..0cac0d6 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2019 Jim Bennett +# Copyright (c) 2020 Jim Bennett # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -35,7 +35,7 @@ import circuitpython_base64 as base64 import circuitpython_hmac as hmac import circuitpython_parse as parse -from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager +import adafruit_requests as requests import adafruit_logging as logging from adafruit_logging import Logger import adafruit_hashlib as hashlib @@ -74,25 +74,21 @@ def _parse_http_status(status_code: int, status_reason: str) -> None: if error == status_code: raise DeviceRegistrationError("Error {0}: {1}".format(status_code, status_reason)) - def __init__(self, wifi_manager: ESPSPI_WiFiManager, id_scope: str, device_id: str, key: str, logger: Logger = None): + def __init__(self, socket, id_scope: str, device_id: str, key: str, logger: Logger = None): """Creates an instance of the device registration service - :param wifi_manager: WiFiManager object from ESPSPI_WiFiManager. + :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 adafruit_logging.Logger logger: The logger to use to log messages - :raises TypeError: if the WiFi manager is not the right type """ - wifi_type = str(type(wifi_manager)) - if "ESPSPI_WiFiManager" not in wifi_type: - raise TypeError("This library requires a WiFiManager object.") - - self._wifi_manager = wifi_manager self._id_scope = id_scope self._device_id = device_id self._key = key self._logger = logger if logger is not None else logging.getLogger("log") + requests.set_socket(socket) + @staticmethod def compute_derived_symmetric_key(secret: str, msg: str) -> bytes: """Computes a derived symmetric key from a secret and a message @@ -155,7 +151,7 @@ def _run_put_request_with_retry(self, url, body, headers): gc.collect() try: self._logger.debug("Trying to send...") - response = self._wifi_manager.put(url, json=body, headers=headers) + response = requests.put(url, json=body, headers=headers) self._logger.debug("Sent!") break except RuntimeError as runtime_error: @@ -180,7 +176,7 @@ def _run_get_request_with_retry(self, url, headers): gc.collect() try: self._logger.debug("Trying to send...") - response = self._wifi_manager.get(url, headers=headers) + response = requests.get(url, headers=headers) self._logger.debug("Sent!") break except RuntimeError as runtime_error: diff --git a/adafruit_azureiot/iot_error.py b/adafruit_azureiot/iot_error.py index 1f3631b..767351c 100644 --- a/adafruit_azureiot/iot_error.py +++ b/adafruit_azureiot/iot_error.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2019 Jim Bennett +# Copyright (c) 2020 Jim Bennett # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 926f03c..7192014 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2019 Jim Bennett +# Copyright (c) 2020 Jim Bennett # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -31,8 +31,6 @@ import gc import json import time -import adafruit_esp32spi.adafruit_esp32spi_socket as socket -from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager import adafruit_minimqtt as minimqtt from adafruit_minimqtt import MQTT import circuitpython_parse as parse @@ -118,7 +116,7 @@ def _gen_sas_token(self) -> str: # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/25 def _try_create_mqtt_client(self, hostname: str) -> None: - minimqtt.set_socket(socket, self._wifi_manager.esp) + minimqtt.set_socket(self._socket, self._iface) self._mqtts = MQTT( broker=hostname, @@ -338,7 +336,8 @@ def _get_device_settings(self) -> None: def __init__( self, callback: IoTMQTTCallback, - wifi_manager: ESPSPI_WiFiManager, + socket, + iface, hostname: str, device_id: str, key: str, @@ -346,8 +345,9 @@ def __init__( logger: logging = None, ): """Create the Azure IoT MQTT client - :param wifi_manager: The WiFi manager :param IoTMQTTCallback callback: A callback class + :param socket: The socket to communicate over + :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 @@ -355,7 +355,8 @@ def __init__( :param adafruit_logging logger: The logger """ self._callback = callback - self._wifi_manager = wifi_manager + self._socket = socket + self._iface = iface self._mqtt_connected = False self._auth_response_received = False self._mqtts = None @@ -368,6 +369,15 @@ def __init__( self._logger = logger if logger is not None else logging.getLogger("log") self._is_subscribed_to_twins = False + def _subscribe_to_core_topics(self): + self._mqtts.subscribe("devices/{}/messages/events/#".format(self._device_id)) + self._mqtts.subscribe("devices/{}/messages/devicebound/#".format(self._device_id)) + self._mqtts.subscribe("$iothub/methods/#") + + def _subscribe_to_twin_topics(self): + self._mqtts.subscribe("$iothub/twin/PATCH/properties/desired/#") # twin desired property changes + self._mqtts.subscribe("$iothub/twin/res/#") # twin properties response + def connect(self) -> bool: """Connects to the MQTT broker :returns: True if the connection is successful, otherwise False @@ -388,9 +398,7 @@ def connect(self) -> bool: self._mqtt_connected = True self._auth_response_received = True - self._mqtts.subscribe("devices/{}/messages/events/#".format(self._device_id)) - self._mqtts.subscribe("devices/{}/messages/devicebound/#".format(self._device_id)) - self._mqtts.subscribe("$iothub/methods/#") + self._subscribe_to_core_topics() return True @@ -402,8 +410,7 @@ def subscribe_to_twins(self) -> None: return # do this separately as this is not supported in B1 hubs - self._mqtts.subscribe("$iothub/twin/PATCH/properties/desired/#") # twin desired property changes - self._mqtts.subscribe("$iothub/twin/res/#") # twin properties response + self._subscribe_to_twin_topics() self._get_device_settings() @@ -419,6 +426,44 @@ def disconnect(self) -> None: self._mqtt_connected = False self._mqtts.disconnect() + # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/26 + def _fix_broker_name(self, hostname): + try: # set broker IP + self._mqtts.broker = self._iface.unpretty_ip(hostname) + except ValueError: # set broker URL + self._mqtts.broker = hostname + + def reconnect(self) -> None: + """Reconnects to the MQTT broker + """ + self._logger.info("- iot_mqtt :: reconnect :: ") + + self._auth_response_received = None + + # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/25 + # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/26 + # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/28 + try: + self._fix_broker_name(self._hostname) + self._mqtts.connect() + except ValueError: + self._fix_broker_name("https://" + self._hostname) + self._mqtts.connect() + + self._logger.info("- iot_mqtt :: waiting for auth...") + + while self._auth_response_received is None: + self.loop() + + self._logger.info("- iot_mqtt :: authed, subscribing...") + + # Resubscribe + self._subscribe_to_core_topics() + if self._is_subscribed_to_twins: + self._subscribe_to_twin_topics() + + self._logger.info("- iot_mqtt :: resubscribed") + 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 diff --git a/adafruit_azureiot/iotcentral_device.py b/adafruit_azureiot/iotcentral_device.py index acf0103..43270cc 100644 --- a/adafruit_azureiot/iotcentral_device.py +++ b/adafruit_azureiot/iotcentral_device.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2019 Jim Bennett +# Copyright (c) 2020 Jim Bennett # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,6 @@ import json import time -from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager import adafruit_logging as logging from .device_registration import DeviceRegistration from .iot_error import IoTError @@ -87,23 +86,23 @@ def device_twin_reported_updated(self, reported_property_name: str, reported_pro self.on_property_changed(reported_property_name, reported_property_value, reported_version) # pylint: disable=R0913 - def __init__( - self, wifi_manager: ESPSPI_WiFiManager, id_scope: str, device_id: str, key: str, token_expires: int = 21600, logger: logging = None - ): + def __init__(self, socket, iface, id_scope: str, device_id: str, key: str, token_expires: int = 21600, logger: logging = None): """Create the Azure IoT Central device client - :param wifi_manager: The WiFi manager + :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 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 """ - self._wifi_manager = wifi_manager + self._socket = socket + self._iface = iface self._id_scope = id_scope self._device_id = device_id self._key = key self._token_expires = token_expires - self._logger = logger + self._logger = logger if logger is not None else logging.getLogger("log") self._device_registration = None self._mqtt = None @@ -133,11 +132,11 @@ def connect(self) -> None: :raises DeviceRegistrationError: if the device cannot be registered successfully :raises RuntimeError: if the internet connection is not responding or is unable to connect """ - self._device_registration = DeviceRegistration(self._wifi_manager, self._id_scope, self._device_id, self._key, self._logger) + self._device_registration = DeviceRegistration(self._socket, self._id_scope, self._device_id, self._key, self._logger) token_expiry = int(time.time() + self._token_expires) hostname = self._device_registration.register_device(token_expiry) - self._mqtt = IoTMQTT(self, self._wifi_manager, hostname, self._device_id, self._key, self._token_expires, self._logger) + self._mqtt = IoTMQTT(self, self._socket, self._iface, hostname, self._device_id, self._key, self._token_expires, self._logger) self._mqtt.connect() self._mqtt.subscribe_to_twins() @@ -151,6 +150,14 @@ def disconnect(self) -> None: self._mqtt.disconnect() + def reconnect(self) -> None: + """Reconnects to the MQTT broker + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + self._mqtt.reconnect() + 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 diff --git a/adafruit_azureiot/iothub_device.py b/adafruit_azureiot/iothub_device.py index db0cb78..c7900d6 100755 --- a/adafruit_azureiot/iothub_device.py +++ b/adafruit_azureiot/iothub_device.py @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2019 Jim Bennett +# Copyright (c) 2020 Jim Bennett, Elena Horton # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,6 @@ """ import json -from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager import adafruit_logging as logging from .iot_error import IoTError from .iot_mqtt import IoTMQTT, IoTMQTTCallback, IoTResponse @@ -129,8 +128,16 @@ def device_twin_reported_updated(self, reported_property_name: str, reported_pro # pylint: disable=E1102 self._on_device_twin_reported_updated(reported_property_name, reported_property_value, reported_version) - def __init__(self, wifi_manager: ESPSPI_WiFiManager, device_connection_string: str, token_expires: int = 21600, logger: logging = None): - self._wifi_manager = wifi_manager + def __init__(self, socket, iface, device_connection_string: str, token_expires: int = 21600, logger: logging = 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 + """ + self._socket = socket + self._iface = iface self._token_expires = token_expires self._logger = logger if logger is not None else logging.getLogger("log") @@ -260,7 +267,7 @@ def connect(self) -> None: :raises RuntimeError: if the internet connection is not responding or is unable to connect """ self._mqtt = IoTMQTT( - self, self._wifi_manager, self._hostname, self._device_id, self._shared_access_key, self._token_expires, self._logger + self, self._socket, self._iface, self._hostname, self._device_id, self._shared_access_key, self._token_expires, self._logger ) self._mqtt.connect() @@ -276,6 +283,14 @@ def disconnect(self) -> None: self._mqtt.disconnect() + def reconnect(self) -> None: + """Reconnects to the MQTT broker + """ + if self._mqtt is None: + raise IoTError("You are not connected to IoT Central") + + self._mqtt.reconnect() + 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 diff --git a/examples/iotcentral_commands.py b/examples/iotcentral_commands.py index efe39dd..14682cf 100644 --- a/examples/iotcentral_commands.py +++ b/examples/iotcentral_commands.py @@ -2,7 +2,9 @@ import board import busio from digitalio import DigitalInOut +import neopixel from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file @@ -25,15 +27,35 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +print("Connecting to WiFi...") + wifi.connect() +print("Connected to WiFi!") + +print("Getting the time...") + ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: time.sleep(5) ntp.set_time() +print("Time:", str(time.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. @@ -63,7 +85,7 @@ from adafruit_azureiot.iot_mqtt import IoTResponse # Create an IoT Hub device client and connect -device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"]) +device = IoTCentralDevice(socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"]) # Subscribe to commands # Commands can be sent from the devices Dashboard in IoT Central, assuming @@ -76,12 +98,26 @@ def command_executed(command_name: str, payload) -> IoTResponse: return IoTResponse(200, "OK") +# Subscribe to the command execute event device.on_command_executed = command_executed +print("Connecting to Azure IoT Central...") + +# Connect to IoT Central device.connect() +print("Connected to Azure IoT Central!") + while True: - # Poll every second for messages from the cloud - device.loop() + 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/iotcentral_notconnected.py b/examples/iotcentral_notconnected.py index 190d6fc..778bfaf 100644 --- a/examples/iotcentral_notconnected.py +++ b/examples/iotcentral_notconnected.py @@ -4,7 +4,9 @@ import board import busio from digitalio import DigitalInOut +import neopixel from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file @@ -27,15 +29,35 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +print("Connecting to WiFi...") + wifi.connect() +print("Connected to WiFi!") + +print("Getting the time...") + ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: time.sleep(5) ntp.set_time() +print("Time:", str(time.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. @@ -64,7 +86,7 @@ from adafruit_azureiot import IoTCentralDevice, IoTError # Create an IoT Hub device client and connect -device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"]) +device = IoTCentralDevice(socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"]) # don't connect # device.connect() diff --git a/examples/iotcentral_properties.py b/examples/iotcentral_properties.py index 03d6f80..fbbddae 100644 --- a/examples/iotcentral_properties.py +++ b/examples/iotcentral_properties.py @@ -3,7 +3,9 @@ import board import busio from digitalio import DigitalInOut +import neopixel from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file @@ -26,15 +28,35 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +print("Connecting to WiFi...") + wifi.connect() +print("Connected to WiFi!") + +print("Getting the time...") + ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: time.sleep(5) ntp.set_time() +print("Time:", str(time.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. @@ -63,7 +85,7 @@ from adafruit_azureiot import IoTCentralDevice # Create an IoT Hub device client and connect -device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"]) +device = IoTCentralDevice(socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"]) # Subscribe to property changes # Properties can be updated either in code, or by adding a form to the view @@ -72,22 +94,36 @@ 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...") + +# Connect to IoT Central device.connect() +print("Connected to Azure IoT Central!") + message_counter = 60 while True: - # 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 = message_counter + 1 - - # Poll every second for messages from the cloud - device.loop() + 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 = 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/iotcentral_simpletest.py b/examples/iotcentral_simpletest.py index 66e55c2..25d08a3 100644 --- a/examples/iotcentral_simpletest.py +++ b/examples/iotcentral_simpletest.py @@ -4,7 +4,9 @@ import board import busio from digitalio import DigitalInOut +import neopixel from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file @@ -27,15 +29,35 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +print("Connecting to WiFi...") + wifi.connect() +print("Connected to WiFi!") + +print("Getting the time...") + ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: time.sleep(5) ntp.set_time() +print("Time:", str(time.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. @@ -64,22 +86,36 @@ from adafruit_azureiot import IoTCentralDevice # Create an IoT Hub device client and connect -device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"]) +device = IoTCentralDevice(socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"]) + +print("Connecting to Azure IoT Central...") + +# Connect to IoT Central device.connect() +print("Connected to Azure IoT Central!") + message_counter = 60 while True: - # 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 = message_counter + 1 - - # Poll every second for messages from the cloud - device.loop() + 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 = 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/iothub_directmethods.py b/examples/iothub_directmethods.py index c029e2e..8f87ee4 100644 --- a/examples/iothub_directmethods.py +++ b/examples/iothub_directmethods.py @@ -2,7 +2,9 @@ import board import busio from digitalio import DigitalInOut +import neopixel from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file @@ -25,15 +27,35 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +print("Connecting to WiFi...") + wifi.connect() +print("Connected to WiFi!") + +print("Getting the time...") + ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: time.sleep(5) ntp.set_time() +print("Time:", str(time.time())) + # You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: @@ -58,7 +80,7 @@ from adafruit_azureiot.iot_mqtt import IoTResponse # Create an IoT Hub device client and connect -device = IoTHubDevice(wifi, secrets["device_connection_string"]) +device = IoTHubDevice(socket, 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, @@ -71,12 +93,26 @@ def direct_method_invoked(method_name: str, payload) -> IoTResponse: 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: - # Poll every second for messages from the cloud - device.loop() + 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/iothub_messages.py b/examples/iothub_messages.py index f5368f2..1b88888 100644 --- a/examples/iothub_messages.py +++ b/examples/iothub_messages.py @@ -4,7 +4,9 @@ import board import busio from digitalio import DigitalInOut +import neopixel from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file @@ -27,15 +29,35 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +print("Connecting to WiFi...") + wifi.connect() +print("Connected to WiFi!") + +print("Getting the time...") + ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: time.sleep(5) ntp.set_time() +print("Time:", str(time.time())) + # You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: @@ -59,7 +81,7 @@ from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect -device = IoTHubDevice(wifi, secrets["device_connection_string"]) +device = IoTHubDevice(socket, 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, @@ -68,24 +90,38 @@ 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...") + +# Connect to IoT Central device.connect() +print("Connected to Azure IoT Hub!") + message_counter = 60 while True: - # 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 = message_counter + 1 - - # Poll every second for messages from the cloud - device.loop() + 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 = 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/iothub_simpletest.py b/examples/iothub_simpletest.py index fc27a6d..49f7518 100644 --- a/examples/iothub_simpletest.py +++ b/examples/iothub_simpletest.py @@ -4,7 +4,9 @@ import board import busio from digitalio import DigitalInOut +import neopixel from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file @@ -27,15 +29,35 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +print("Connecting to WiFi...") + wifi.connect() +print("Connected to WiFi!") + +print("Getting the time...") + ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: time.sleep(5) ntp.set_time() +print("Time:", str(time.time())) + # You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: @@ -59,23 +81,37 @@ from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect -device = IoTHubDevice(wifi, secrets["device_connection_string"]) +device = IoTHubDevice(socket, 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: - # 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 = message_counter + 1 - - # Poll every second for messages from the cloud - device.loop() + 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 = 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/iothub_twin_operations.py b/examples/iothub_twin_operations.py index 4627616..179cd7c 100644 --- a/examples/iothub_twin_operations.py +++ b/examples/iothub_twin_operations.py @@ -3,7 +3,9 @@ import board import busio from digitalio import DigitalInOut +import neopixel from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_ntp import NTP # Get wifi details and more from a secrets.py file @@ -26,15 +28,35 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +"""Use below for Most Boards""" +status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) # Uncomment for Most Boards +"""Uncomment below for ItsyBitsy M4""" +# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) +# Uncomment below for an externally defined RGB LED +# import adafruit_rgbled +# from adafruit_esp32spi import PWMOut +# RED_LED = PWMOut.PWMOut(esp, 26) +# GREEN_LED = PWMOut.PWMOut(esp, 27) +# BLUE_LED = PWMOut.PWMOut(esp, 25) +# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) + +print("Connecting to WiFi...") + wifi.connect() +print("Connected to WiFi!") + +print("Getting the time...") + ntp = NTP(esp) # Wait for a valid time to be received while not ntp.valid_time: time.sleep(5) ntp.set_time() +print("Time:", str(time.time())) + # You will need an Azure subscription to create an Azure IoT Hub resource # # If you don't have an Azure subscription: @@ -61,7 +83,7 @@ from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect -device = IoTHubDevice(wifi, secrets["device_connection_string"]) +device = IoTHubDevice(socket, 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 @@ -71,24 +93,38 @@ def device_twin_desired_updated(desired_property_name: str, desired_property_val 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...") + +# Connect to IoT Central device.connect() +print("Connected to Azure IoT Hub!") + message_counter = 60 while True: - 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 = message_counter + 1 - - # Poll every second for messages from the cloud - device.loop() + 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 = 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) From e54804e264627097e8431415b40091358deddcd9 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Fri, 17 Apr 2020 14:30:07 -0700 Subject: [PATCH 24/33] Update requirements.txt --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ffec0c2..711349f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ Adafruit-Blinka -Adafruit_CircuitPython_ESP32SPI Adafruit-CircuitPython-miniMQTT CircuitPython-HMAC CircuitPython-Base64 From dbc03d665c9a3a94e4be212f32c1333f4ab02449 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Fri, 17 Apr 2020 14:39:40 -0700 Subject: [PATCH 25/33] Fixing the build script --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 43e3d14..827258f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,7 +48,7 @@ jobs: black --check --target-version=py35 --line-length=140 . - name: PyLint run: | - pylint $( find . -path './adafruit*.py' ) + pylint $( find . -path './adafruit_azureiot/*.py' ) ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace,wrong-import-position $( find . -path "./examples/*.py" )) - name: Build assets run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . From 1f3d37b62f517cd6631ceff726f1bfe15094e11b Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Wed, 22 Apr 2020 16:00:22 -0700 Subject: [PATCH 26/33] Fixing reconnect logic after bug fixes in mini MQTT --- adafruit_azureiot/iot_mqtt.py | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/adafruit_azureiot/iot_mqtt.py b/adafruit_azureiot/iot_mqtt.py index 7192014..62537cb 100644 --- a/adafruit_azureiot/iot_mqtt.py +++ b/adafruit_azureiot/iot_mqtt.py @@ -426,43 +426,12 @@ def disconnect(self) -> None: self._mqtt_connected = False self._mqtts.disconnect() - # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/26 - def _fix_broker_name(self, hostname): - try: # set broker IP - self._mqtts.broker = self._iface.unpretty_ip(hostname) - except ValueError: # set broker URL - self._mqtts.broker = hostname - def reconnect(self) -> None: """Reconnects to the MQTT broker """ self._logger.info("- iot_mqtt :: reconnect :: ") - self._auth_response_received = None - - # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/25 - # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/26 - # Workaround for https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/issues/28 - try: - self._fix_broker_name(self._hostname) - self._mqtts.connect() - except ValueError: - self._fix_broker_name("https://" + self._hostname) - self._mqtts.connect() - - self._logger.info("- iot_mqtt :: waiting for auth...") - - while self._auth_response_received is None: - self.loop() - - self._logger.info("- iot_mqtt :: authed, subscribing...") - - # Resubscribe - self._subscribe_to_core_topics() - if self._is_subscribed_to_twins: - self._subscribe_to_twin_topics() - - self._logger.info("- iot_mqtt :: resubscribed") + self._mqtts.reconnect() def is_connected(self) -> bool: """Gets if there is an open connection to the MQTT broker From e1e13c06a681958ded707325fef315e10c03cc8b Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Thu, 23 Apr 2020 15:00:03 -0700 Subject: [PATCH 27/33] Tweaked the samples to have more details on library dependencies --- README.rst | 1 - examples/iotcentral_commands.py | 11 ++++++++++- examples/iotcentral_notconnected.py | 11 ++++++++++- examples/iotcentral_properties.py | 11 ++++++++++- examples/iotcentral_simpletest.py | 11 ++++++++++- examples/iothub_directmethods.py | 11 ++++++++++- examples/iothub_messages.py | 11 ++++++++++- examples/iothub_simpletest.py | 11 ++++++++++- examples/iothub_twin_operations.py | 11 ++++++++++- 9 files changed, 80 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index aa2a31d..25bdd47 100644 --- a/README.rst +++ b/README.rst @@ -44,7 +44,6 @@ Dependencies This driver depends on: * `Adafruit CircuitPython `_ -* `Adafruit CircuitPython ESP32 SPI `_ * `Adafruit CircuitPython MiniMQTT `_ * `CircuitPython Base64 `_ diff --git a/examples/iotcentral_commands.py b/examples/iotcentral_commands.py index 14682cf..e8c3a56 100644 --- a/examples/iotcentral_commands.py +++ b/examples/iotcentral_commands.py @@ -80,7 +80,16 @@ # '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 +# +# From the CircuitPython Community LIbrary and Driver Bundle (https://github.com/adafruit/CircuitPython_Community_Bundle): +# * circuitpython-hmac +# * circuitpython-base64 +# * circuitpython-parse from adafruit_azureiot import IoTCentralDevice from adafruit_azureiot.iot_mqtt import IoTResponse diff --git a/examples/iotcentral_notconnected.py b/examples/iotcentral_notconnected.py index 778bfaf..4a9f4d6 100644 --- a/examples/iotcentral_notconnected.py +++ b/examples/iotcentral_notconnected.py @@ -82,7 +82,16 @@ # '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 +# +# From the CircuitPython Community LIbrary and Driver Bundle (https://github.com/adafruit/CircuitPython_Community_Bundle): +# * circuitpython-hmac +# * circuitpython-base64 +# * circuitpython-parse from adafruit_azureiot import IoTCentralDevice, IoTError # Create an IoT Hub device client and connect diff --git a/examples/iotcentral_properties.py b/examples/iotcentral_properties.py index fbbddae..15dde99 100644 --- a/examples/iotcentral_properties.py +++ b/examples/iotcentral_properties.py @@ -81,7 +81,16 @@ # '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 +# +# From the CircuitPython Community LIbrary and Driver Bundle (https://github.com/adafruit/CircuitPython_Community_Bundle): +# * circuitpython-hmac +# * circuitpython-base64 +# * circuitpython-parse from adafruit_azureiot import IoTCentralDevice # Create an IoT Hub device client and connect diff --git a/examples/iotcentral_simpletest.py b/examples/iotcentral_simpletest.py index 25d08a3..571ee38 100644 --- a/examples/iotcentral_simpletest.py +++ b/examples/iotcentral_simpletest.py @@ -82,7 +82,16 @@ # '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 +# +# From the CircuitPython Community LIbrary and Driver Bundle (https://github.com/adafruit/CircuitPython_Community_Bundle): +# * circuitpython-hmac +# * circuitpython-base64 +# * circuitpython-parse from adafruit_azureiot import IoTCentralDevice # Create an IoT Hub device client and connect diff --git a/examples/iothub_directmethods.py b/examples/iothub_directmethods.py index 8f87ee4..341a51f 100644 --- a/examples/iothub_directmethods.py +++ b/examples/iothub_directmethods.py @@ -75,7 +75,16 @@ # # 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 +# +# From the CircuitPython Community LIbrary and Driver Bundle (https://github.com/adafruit/CircuitPython_Community_Bundle): +# * circuitpython-hmac +# * circuitpython-base64 +# * circuitpython-parse from adafruit_azureiot import IoTHubDevice from adafruit_azureiot.iot_mqtt import IoTResponse diff --git a/examples/iothub_messages.py b/examples/iothub_messages.py index 1b88888..52524b1 100644 --- a/examples/iothub_messages.py +++ b/examples/iothub_messages.py @@ -77,7 +77,16 @@ # # 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 +# +# From the CircuitPython Community LIbrary and Driver Bundle (https://github.com/adafruit/CircuitPython_Community_Bundle): +# * circuitpython-hmac +# * circuitpython-base64 +# * circuitpython-parse from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect diff --git a/examples/iothub_simpletest.py b/examples/iothub_simpletest.py index 49f7518..703190c 100644 --- a/examples/iothub_simpletest.py +++ b/examples/iothub_simpletest.py @@ -77,7 +77,16 @@ # # 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 +# +# From the CircuitPython Community LIbrary and Driver Bundle (https://github.com/adafruit/CircuitPython_Community_Bundle): +# * circuitpython-hmac +# * circuitpython-base64 +# * circuitpython-parse from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect diff --git a/examples/iothub_twin_operations.py b/examples/iothub_twin_operations.py index 179cd7c..83ef88b 100644 --- a/examples/iothub_twin_operations.py +++ b/examples/iothub_twin_operations.py @@ -79,7 +79,16 @@ # # To us twins, you will need either a free or standard tier IoT Hub. Basic tier doesn't # support twins - +# +# 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 +# +# From the CircuitPython Community LIbrary and Driver Bundle (https://github.com/adafruit/CircuitPython_Community_Bundle): +# * circuitpython-hmac +# * circuitpython-base64 +# * circuitpython-parse from adafruit_azureiot import IoTHubDevice # Create an IoT Hub device client and connect From a9833dd7752b5b955634ebfdf7bcc84443e321da Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Wed, 29 Apr 2020 16:13:37 -0700 Subject: [PATCH 28/33] Adding more documentation around the keys --- README.rst | 36 ++++++++++++++++++++++-- examples/azureiot_secrets_example.py | 38 ++++++++++++++++++++++++++ images/iot-central-connect-button.png | Bin 0 -> 19120 bytes images/iot-central-connect-dialog.png | Bin 0 -> 74478 bytes images/iot-hub-device-keys.png | Bin 0 -> 72070 bytes images/iot-hub-device.png | Bin 0 -> 70638 bytes 6 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 examples/azureiot_secrets_example.py create mode 100644 images/iot-central-connect-button.png create mode 100644 images/iot-central-connect-dialog.png create mode 100644 images/iot-hub-device-keys.png create mode 100644 images/iot-hub-device.png diff --git a/README.rst b/README.rst index 25bdd47..9dbf590 100644 --- a/README.rst +++ b/README.rst @@ -85,7 +85,16 @@ To interact with Azure IoT Hub, you will need to create a hub, and a register a - Open the `Azure Portal `_. - Follow the instructions in `Microsoft Docs `_ to create an Azure IoT Hub and register a device. -- Copy the devices Primary Key, and add this to your ``secrets.py`` file. +- Copy the devices Primary or secondary connection string, and add this to your ``secrets.py`` file. + +You can find the device connection string by selecting the IoT Hub in the `Azure Portal `_, *selecting Explorer -> IoT devices*, then selecting your device. + +:: images/iot-hub-device.png + :alt: Locating the device in the IoT hub blade + +Then copy either the primary or secondary connection string using the copy button next to the value. + +:: images/iot-hub-device-keys.png **Connect your device to Azure IoT Hub** @@ -162,7 +171,30 @@ 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 Primary 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 Key in your ``secrets.py`` file. + +.. image:: images/iot-central-connect-button.png + :alt: The IoT Central connect button + +*The connect button* + +.. image:: images/iot-central-connect-dialog.png + :alt: The IoT Central connection details dialog + +*The connection details dialog* + +.. code-block:: python + + secrets = { + # WiFi settings + "ssid": "", + "password": "", + + # Azure IoT Central settings + "id_scope": "", + "device_id": "", + "key": "" + } **Connect your device to your Azure IoT Central app** diff --git a/examples/azureiot_secrets_example.py b/examples/azureiot_secrets_example.py new file mode 100644 index 0000000..e7ac012 --- /dev/null +++ b/examples/azureiot_secrets_example.py @@ -0,0 +1,38 @@ +# This file is where you keep secret settings, passwords, and tokens! +# If you put them in the code you risk committing that info or sharing it +# which would be not great. So, instead, keep it all in this one file and +# keep it a secret. + +# To find out how to hide any changes you make to this file from Git, check out +# this blog post: https://www.jimbobbennett.io/hiding-api-keys-from-git/ + +""" +Contains the secrets for your app including WiFi connection details. +DO NOT CHECK THIS INTO SOURCE CODE CONTROL!!!!11!!! +""" + +secrets = { + # WiFi settings + "ssid": "", + "password": "", + + # Azure IoT Central settings - if you are connecting to Azure IoT Central, fill in these three values + # To get these values, select your device in Azure IoT Central, + # then select the Connect button + # A dialog will appear with these three values + # id_scope comes from the ID scope value + # device_id comes from the Device ID value + # key comes from either the Primary key or Secondary key + "id_scope": "", + "device_id": "", + "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 + # connection string using the copy button next to the value and set this here. + # It will be in the format: + # HostName=.azure-devices.net;DeviceId=;SharedAccessKey= + # Note - you need the primary or secondary connection string, NOT the primary or secondary key + "device_connection_string": "", +} \ No newline at end of file diff --git a/images/iot-central-connect-button.png b/images/iot-central-connect-button.png new file mode 100644 index 0000000000000000000000000000000000000000..006fba30230db678999283b0dcdf835df866133c GIT binary patch literal 19120 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�pq4932bV-Qo!Da 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�M&ax4&e6B+>=BrN?ogq^gz%?qb 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
aKOW*DdabiRJ5=i7dXCo%ofwJ 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 QYv7Nf>|>b%DShAn2cEqX9{>OV literal 0 HcmV?d00001 diff --git a/images/iot-hub-device.png b/images/iot-hub-device.png new file mode 100644 index 0000000000000000000000000000000000000000..e04fd0b7bfcea85cbce7a4f37e088b84d18f6f45 GIT binary patch literal 70638 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 literal 0 HcmV?d00001 From b49197e2405505d2643adfaa43f09e23b7063a4a Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Wed, 29 Apr 2020 16:15:36 -0700 Subject: [PATCH 29/33] Update README.rst --- README.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 9dbf590..02b8382 100644 --- a/README.rst +++ b/README.rst @@ -89,12 +89,17 @@ To interact with Azure IoT Hub, you will need to create a hub, and a register a You can find the device connection string by selecting the IoT Hub in the `Azure Portal `_, *selecting Explorer -> IoT devices*, then selecting your device. -:: images/iot-hub-device.png +.. images/iot-hub-device.png :alt: Locating the device in the IoT hub blade +*Locating the device in the IoT hub blade* + Then copy either the primary or secondary connection string using the copy button next to the value. -:: images/iot-hub-device-keys.png +.. images/iot-hub-device-keys.png + :alt: Copy the primary connection string + +*Copy the primary connection string* **Connect your device to Azure IoT Hub** From 9195773490cdad9f3c37c4c7e59b32620431cd8e Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Wed, 29 Apr 2020 16:17:10 -0700 Subject: [PATCH 30/33] Update README.rst --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 02b8382..875be8c 100644 --- a/README.rst +++ b/README.rst @@ -89,14 +89,14 @@ To interact with Azure IoT Hub, you will need to create a hub, and a register a You can find the device connection string by selecting the IoT Hub in the `Azure Portal `_, *selecting Explorer -> IoT devices*, then selecting your device. -.. images/iot-hub-device.png +.. image:: images/iot-hub-device.png :alt: Locating the device in the IoT hub blade *Locating the device in the IoT hub blade* Then copy either the primary or secondary connection string using the copy button next to the value. -.. images/iot-hub-device-keys.png +.. image:: images/iot-hub-device-keys.png :alt: Copy the primary connection string *Copy the primary connection string* From 90c53ff10f5f1ef2c17e5270147c800a3fc43a50 Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Wed, 29 Apr 2020 16:18:53 -0700 Subject: [PATCH 31/33] Update azureiot_secrets_example.py --- examples/azureiot_secrets_example.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/azureiot_secrets_example.py b/examples/azureiot_secrets_example.py index e7ac012..0875a7e 100644 --- a/examples/azureiot_secrets_example.py +++ b/examples/azureiot_secrets_example.py @@ -15,7 +15,6 @@ # WiFi settings "ssid": "", "password": "", - # Azure IoT Central settings - if you are connecting to Azure IoT Central, fill in these three values # To get these values, select your device in Azure IoT Central, # then select the Connect button @@ -26,7 +25,6 @@ "id_scope": "", "device_id": "", "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 @@ -35,4 +33,4 @@ # HostName=.azure-devices.net;DeviceId=;SharedAccessKey= # Note - you need the primary or secondary connection string, NOT the primary or secondary key "device_connection_string": "", -} \ No newline at end of file +} From 4ef5e132637c05a4ad9c42a85ecaed69a8496d9c Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Wed, 29 Apr 2020 16:29:47 -0700 Subject: [PATCH 32/33] Update device_registration.py --- adafruit_azureiot/device_registration.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_azureiot/device_registration.py b/adafruit_azureiot/device_registration.py index 0cac0d6..dc83ab3 100644 --- a/adafruit_azureiot/device_registration.py +++ b/adafruit_azureiot/device_registration.py @@ -203,7 +203,7 @@ def register_device(self, expiry: int) -> str: :raises DeviceRegistrationError: if the device cannot be registered successfully :raises RuntimeError: if the internet connection is not responding or is unable to connect """ - # pylint: disable=c0103 + # pylint: disable=C0103 sr = self._id_scope + "%2Fregistrations%2F" + self._device_id sig_no_encode = DeviceRegistration.compute_derived_symmetric_key(self._key, sr + "\n" + str(expiry)) sig_encoded = parse.quote(sig_no_encode, "~()*!.'") @@ -237,8 +237,8 @@ def register_device(self, expiry: int) -> str: data = None try: data = response.json() - except ValueError as e: - err = "ERROR: non JSON is received from " + constants.DPS_END_POINT + " => " + str(response) + " .. message : " + str(e) + except ValueError as error: + err = "ERROR: non JSON is received from " + constants.DPS_END_POINT + " => " + str(response) + " .. message : " + str(error) self._logger.error(err) raise DeviceRegistrationError(err) From 571f496fc07db4866cb1136a772ffa7d156be5ed Mon Sep 17 00:00:00 2001 From: Jim Bennett Date: Wed, 29 Apr 2020 16:42:25 -0700 Subject: [PATCH 33/33] Trying to get images in both the readme and docs --- README.rst | 8 ++++---- {images => docs}/iot-central-connect-button.png | Bin {images => docs}/iot-central-connect-dialog.png | Bin {images => docs}/iot-hub-device-keys.png | Bin {images => docs}/iot-hub-device.png | Bin iot-central-connect-button.png | Bin 0 -> 19120 bytes iot-central-connect-dialog.png | Bin 0 -> 74478 bytes iot-hub-device-keys.png | Bin 0 -> 72070 bytes iot-hub-device.png | Bin 0 -> 70638 bytes 9 files changed, 4 insertions(+), 4 deletions(-) rename {images => docs}/iot-central-connect-button.png (100%) rename {images => docs}/iot-central-connect-dialog.png (100%) rename {images => docs}/iot-hub-device-keys.png (100%) rename {images => docs}/iot-hub-device.png (100%) create mode 100644 iot-central-connect-button.png create mode 100644 iot-central-connect-dialog.png create mode 100644 iot-hub-device-keys.png create mode 100644 iot-hub-device.png diff --git a/README.rst b/README.rst index 875be8c..1c7bab7 100644 --- a/README.rst +++ b/README.rst @@ -89,14 +89,14 @@ To interact with Azure IoT Hub, you will need to create a hub, and a register a You can find the device connection string by selecting the IoT Hub in the `Azure Portal `_, *selecting Explorer -> IoT devices*, then selecting your device. -.. image:: images/iot-hub-device.png +.. image:: iot-hub-device.png :alt: Locating the device in the IoT hub blade *Locating the device in the IoT hub blade* Then copy either the primary or secondary connection string using the copy button next to the value. -.. image:: images/iot-hub-device-keys.png +.. image:: iot-hub-device-keys.png :alt: Copy the primary connection string *Copy the primary connection string* @@ -178,12 +178,12 @@ To use Azure IoT Central, you will need to create an Azure IoT Central app, crea - 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. -.. image:: images/iot-central-connect-button.png +.. image:: iot-central-connect-button.png :alt: The IoT Central connect button *The connect button* -.. image:: images/iot-central-connect-dialog.png +.. image:: iot-central-connect-dialog.png :alt: The IoT Central connection details dialog *The connection details dialog* diff --git a/images/iot-central-connect-button.png b/docs/iot-central-connect-button.png similarity index 100% rename from images/iot-central-connect-button.png rename to docs/iot-central-connect-button.png diff --git a/images/iot-central-connect-dialog.png b/docs/iot-central-connect-dialog.png similarity index 100% rename from images/iot-central-connect-dialog.png rename to docs/iot-central-connect-dialog.png diff --git a/images/iot-hub-device-keys.png b/docs/iot-hub-device-keys.png similarity index 100% rename from images/iot-hub-device-keys.png rename to docs/iot-hub-device-keys.png diff --git a/images/iot-hub-device.png b/docs/iot-hub-device.png similarity index 100% rename from images/iot-hub-device.png rename to docs/iot-hub-device.png diff --git a/iot-central-connect-button.png b/iot-central-connect-button.png new file mode 100644 index 0000000000000000000000000000000000000000..006fba30230db678999283b0dcdf835df866133c GIT binary patch literal 19120 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�pq4932bV-Qo!Da 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�M&ax4&e6B+>=BrN?ogq^gz%?qb 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
aKOW*DdabiRJ5=i7dXCo%ofwJ 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 QYv7Nf>|>b%DShAn2cEqX9{>OV literal 0 HcmV?d00001 diff --git a/iot-hub-device.png b/iot-hub-device.png new file mode 100644 index 0000000000000000000000000000000000000000..e04fd0b7bfcea85cbce7a4f37e088b84d18f6f45 GIT binary patch literal 70638 zcmY(L1y~f_`~CrukOrk238f^Y8>CYdM7pILmX?t2?oR2hrMnRnmX0N)JD2zmufO-} z`@b$?8FuE(oH_HH=lR_C*TF5dQry zLR{99|GhqWeB;(^_Zl3W2%LquZKgQ*YetxAT9#)`o!y#C z7{P*5cjvqxe;G&cul9gdm3VuNs3{TtenqkPi25OGUaKk!4K`=I7ENXm$R~iu!T$H_ zlTucxNYGc5zh4~%*il&v&)w$K5hX`G&)F&8!v;|MAUOJ5N z_A3UD^0$2Xks~qqto1&WwXM?oZdaGf>-I`)E1T@kDTdJdoTQe_oo&M1DC&rK4asnb06(`Ce5(dYNcQL$=yp! zxDIh+XZd`G{<+)su_Bw=QmAt>JnoZu7*zYNr<+Z2ot514rlToj<5aiFp1#|fwUFu0 zmMvBSt@!qVaO9N_cZc5PVnMPRJBTT7E%32qXynKF4+ThgY_kOKub|VpQdpX;Zgs<@ zpXbOe1)63n*^P%Ff|m^Uo!>UFMnP z$WS&NGO=ReyR%p-hR+SwWXs`ht-!ru(VaYKyiI$3` zQ`jrYji?4Ltams&oWlFIN`-r_5g5=-_nCS9M`@lXgY=T#?`|>`Q#c28-1pyh^Vb2L z4(UXu*gMJKbNcRicRs_iFznSGfKjtORj3(?&s=H;?K>8@Sa48M&#Y*>8N%fIO*wCz zs4aaU*0y&#qwC#t>%3qm#lKhCZ5hg3Z#zGAC*}E@k>6sPDY2g_kTixBQ6VX<9Ls91 z*3JsrXxXsn?7SW=RiODnc6$|Bs!i!l+57wui%j9o$}RWpGP9`y>#FzpvgP;1_A4zt zmq_qK#|47xB)>-eYH1|HZ2Q}s_va2^12$g|m$ov!trsZw+F%Fuztl9Z&6}W{)(bo( zpXQ#Be5LZ@%b9?rC{jo373*k|)QB+xqd$=|D}T z^XS`|iD0HxM(6A>Q_orB#CHYmo5`kYW(EedHn9DMNO>vV&f2O_>&aR+r4Itj5V+U@ z0mHjrCk9Iepk^bqs$xZDD;k_)19|heLx) zY}H8WoN^Iy7&CxyFu=~h{mCV1*=a^Q+RTvqscThg*;PG(t_Pd0%)QI1hHFIUA?xzF zw#(w%j6jbXGs``f21 z{tATd+cEi779u9$0)wdp#xJHLgzrJHgO)=kI{!5dEwAg1u~c0C*JF&Xzdow-+RPos zdN){9v`*)@TyLhPBL{M_`QTA3665iX<;sBAE%RCrns;Lgdhx!oRYLP(743)FI=8?a zVZ{9gzMV_%hk^}hd&?uq9O&j^FuEV4bEWuRBtM*t!UVgQOI;{$k%8eo0ng7hc z9R+v;1%W56`rRit50_0>!|n{b4fiffjk;c}KSMo_?bou=Hx)FtlP+z0#mZc(qVzmw zKD7&PF?WjNk|gg>!(1{4+;64*E?+w1M7V!~ER!r1Bth)w!Su{YTy)bO@3)e3SgL4< zFHQQo+K!tMTlK3#i#5G>iVK_X48&+{?r*QJb*iFThWY8xp{_lMAHf3cQAI+Q-bbCt zP}qB~iCuiW453hPlC8sbk-Pkx#`D@m_jm*S3(%Y%IY?L#wsMn{G_qA3iW&jBUz*>F z%-kYgSS}1k)o7qtTFBZOcb~j%6Jm`pW#Xi$u=wfA+D`#1;w(Mg)?3?!M$+&4lG_T5 z-a}x9#3M5^<1^fi%;vKVBuchSMr2C3c$TPbCU2d9Ni~m!|lb>0^o7uYE!$Mpwn_ zhDjGTo|6}c!~s%aZ7ye*IS*9iE%$e@mh)?e%ElQcXLv_X_&a9-Ezn8;CfSbqhMb(P zkWF5AFzU?skBZ!7LP_o4nbMi;H-M(Q+rLqJ5r{;V!`9P=^BZG!w72{b-_%#L66pWTRD%_!MRpCu;ew z66*c)B5A89n5Z77V7;q-lxrR$e19An9)q_dPKp=~`1xYXAY)+XXR)E6!K#yR zM}Tb}0aKe$XXkU3Yl&a|p{+${olaqEif^rK+x4!}+-?Ls27VS|SfB2`cfGWtXZ8zx zR^9Bhiy!xrX8wBY+DB1HTo^aw5K@q0<8sv!E?uEfW8#51`evpw>hydCH1Y21A?1?< zOo5{g5K6i=`ctAORbBfShkjS`AB4CW9Who4P_s#POLlcCfZOQtJ;&DThUuSlT)7qz zh{e@kQr|aDb}6ABXAlYSYN2>ORD9wYcYST>C%HGmF+QSBm_Z))qp&JU%R_U7YF~iw z_(j#z%!GT7yi5G}Y4=(}&#*+S$O!M~jQejvh&O|)-*T(L*7*_uyFt*;^8GJf>$#Di_ zV;4@{jHLHOx~pa#Z%Br|a7y!N{;=fP(yfHP*0MiLl}YJvvx!7hH8QL;5D`e4yUend z=5ZuIcZz$%HK}7Ff|6Q6Pn{yA5inf%z=}X$fGC9JJt@q4u0bHSg}f+8=mdX7c^S&uYTXu)+W-I^%+uyDeT@!8eZd@mFZ71|X+ zu&@&T^_A@DxoFs`@u@a^+w~?%Xn2zIM}0mr?=Un?Zi?y67U>o?v3swq`0>Q;z}pY+ z-V}xgn)VzQ>MUKcP_H-QoN#|Y*OF6HXXP?Gv}#z~L~4j$-kYsi#6VcJ*cm$hJyjS< zva&h6B^gourVWj$2E#{Ahy9--&S!x->Z54WZh1>jSMRQ_j-zq)_1TN=(7sS9-U36N z1xjkvVzz!AKO~Gb@*0z4ZmG2d(M zW4LRTg)?Re=^8Tl)z%uVj#k?vUrsGWkRU>p)qFh*kyBRqxZq_Pj8t)0w*IoMpkaQD zZPu(XeSxjh{RIi>`ZULPQXJoF=$6p!9cvmZ&G*(IC|eMzp(GiedHZSriI#V#gzM8& z<~{pHAy7&(5gauA0K3veNHMH=vYp*h#R{Vk)yw2w)uwIH*3i*uqER%*hB@GOH;vM# z+m=ICGAbw)(g_g+!2`%LIj(V^hU!JgUgVN7jl@ z?$|W8V7vj{5Uw&3uxS#!U!?vgzZERcgOSHq}P}eO9VQg zPWs@68LZk>{);{mo1O-kj}5xX>65(1!Oo&oVXs@d3j8-y9AySCro$-BnM zWo)k_+bs7|s%Wd0J=wlJ_M z#mIhaR-ugd60eMDIlP`YK3QprAFGm0C|s6wFGlO<3=y99^fqZ*)+f&(fH}}?*l2m& zyZr$(5D!EhVou1AY0Xt|Rd9aI50}ZAWU%Kw1_@WCV{4oc#ZoH)LnBLL+IL$K!T_lm zq*MLmZT+LRBWY1EAG(fS3Wabh_Ed|1xZ~23=SvAYTOTgQ{XuBc$W6g6 z3r^+K)Lzd&8>Wp1dlE}H@a>`9U|e~iohrJc+R?Mu(=)a+fM~y`PXEC>&*g!zT`kHa zAo+>d5H4K9?T&OYLgs#T`zZUYYfL=(C9Kr4>xl#?b>giK;f*3vpa-_VvLnG>t zc<9kW%Qdad9+Lk*d^Jf4#7JJtt0PF95J#-qR^=V>%f$>!D8Om<`ziVxUw$-<)U&MG zuOE!r`dRM?YuNa(A?Ip5xt;iFNTvAtPviOiz(a2=iXO=ePCkBJddiME2E5G zKRT!MFDMg1v=0Z~w{bw`92u^s^T5+$BYDDPexp*}P`>54LV$J`5p?FsiAJnJ#^mqA zCSVd}$}Odk-}RhU;9_)~Mp`-rUPN^K`Q2~GK_NF4;!rK4ygj+Gr8iLB?gOmBUbRpn z{|DdSK4pkNtbWbVuu!d*CX+Aw0y;cGGiafT!8X{ly-j>7!5+=2q!&yK=LgwaZf{R@ zh}yE^K;=-<-~}ox=sUJ+UZuk?%WbWi{kf~;pcNl~SN69PK-qM#nG$B2^YlN>z&|#V zd$j=C?sH(0%xCIPq*a_tI~=iuRxc2G|6zxN5q(L?wjDbNU>>jXTB z?R6)ge;Jz)@>V>$O(ws~9chB5)dsy(x%LX;8i}kN*xwY#hIX8lT+pr7J%cSWUK|d0 zhwe6g0EEw_q(|i+L5~+bcmnW4(^mIOXPSne@l4Iml^V(yA}lTUkRx-T)`6wV*<@>Cds_-XpsGM79DI)pd9)|)y6rjoWj zuziDi^t=a)3_!Oe<2zo5t(R$0Mi~kZc~}{CVJ^7hMw2r!<3S21^oQEcv%LWrBy^%g zPt|w-2UbH~JYH^-tnp&A(*avho7V`kOj0wYsEqK)Fl+k)s>(&WkZ1EEzyyR+8?(+B z83HG`bv~hI2dm>n6TN*ia9HT__a+TEA?z-GNzG31Z}x0VUDLq4ut2w=H_8a_XXG-Q z?0HN~q`tPr0Ti}|&uJ%5!au5!`$p1D+;TD!w~9x<3z44iSQ%F+697|k0I_742QVTn zogioH{}}IcO@!1@aUx7@f-_!H_T>+20>_Ak9itfB3o8X@q9~KzF`XPsF|JsC^G8Cs zNT!Q+{bZ(|H)nRLzFIBu_1m#E|h2wtzpDq2!!*tDLj>4q)+9}EVNa25oYfkSVl z18l9`f}T~?!x8mqVe-@5?VyeqB|7yd!x|ANUsq6i3>x|ST+wpaCW?C-wHuTi9x6GFO6CI9ft$J9XN0wpe!!?gS{)tqG%<^5mB0soBq zgdlKT;gQa#Ig81^r))q{91#2!B~R&Vs(d=Z*v5gPssUWk zpA0ET#Ox)ds0MLl^W2~E=bz3ddZ{er=ejYx^FxYa^iqQYI7xP?l^8jeK`j&Bv?&_# z&89hRH~D?ia%1Q3YUMD0526ZBua>JVut1H}a+;|&R*}Awg~>iAIcvpB=G!Z1B$x9Z z1Qc5jF+h6+qNM;l#QyN|FSr>JPr|j?-IKi$WK#(Kiy;WNXUgZr2bfiNALM=S ziSb^uYJIg_#M~r{n@525@lW|(cO6egb;a-O6h#nZ#K)QFq>SaagR<@Byvk$0-~u;D zK1h<}@jmLEVmDrTpO#vd2EtSmIV+A0EY6dwZJ!PmF{8^8<;}nTU9wNDEQjF$ z)$fVy>*#nLOW@aVHiv$9so}p z02u``nJ=gM{an}kZUa&n_bFH+ix5zpn2?ZVK0XJ8B~AqHWsjP4mnGNoinMXF!A!ZfMTuJ3>q>}BEZwZ{Q?zIDg^1LZO{jTZeM1Hc*Em7|I(7y)a?6avxZsvo z+BfEymi=RB|2!ZL9139PN`_ZE?M}boYKgndB?Ee@444|3Har_kHvHGaV@HzM4EIL4 zmurD*>|g9G*J_%yVL5{pW_p=p2^@)J^NghNP63pi{}QNKRDR2vOW_Pq`v6)-P}ogIoaesA{Bg+uQ}Yv9F}z z;qyNV8$f9p5WU%!wQ|jZBRt9Uxpv?X*PC8Yth7-R+81usPv%mKNyZ0xWUm5kY5jf) zAzuTKE3)jgQ4fb*dB7}W%`HYV6TNL3eZ=k39P{%9e zUSJAyN_O5~Z)Hx`nyE7Cwt-vl7M!T8CL71;)7csW_J4 zy5c4v+jy2@ebj&*bz_yu#=kx=if73DFhj5@hB^-a1nE<)?zb;O5z;(Q5e!ne| zC4%5(o>kZ$`evf0k>u_g|vsagd@MPPo?8%^bI04m-&Yw)oo z0d9t{cN?D0E&kDj;MIy>{pFJTJbk9en)S`i*>;VJPHMHm!pN`bTCd*PnlH0OIpHbq zEBhJK*tVn##)>}_$ISPH-<4FfnFA?#?W2-c4nC1>0ovPJbXl&mX}j)=5IB+EU1+K~ zKU`i8CKj;cfzP@6_5I`UK^x4Pgx5ar6hh`WA?gfp>t=f(E7-KYY0}?l#71I8?Q=~% zoB>Kv(=^7_?zh%}%5ZQS%&;~0oU8nsRs#?I`OdG$hhx^x@B`n1Mgsuw=YJNfE;cm+ zD%8Q82+=*{+v!hJ90pm0RBh$9<35s$Nbg&kjifK5WkpL3o9gU|5yVzeG{MIrGNU)HdnF<>q zLu}(Oj!KDDNoM~>(X_oQBA9b>d`iYhA+?$Vzu#}(M7lpzrHi(j%4y9-e*|}cMO=k^pqtOm(In3NpJ1ztpL6u!1!brnl z9AVAqZJBUwi!Ko1HAUvw~8I1$sgAIMHWl1-oLy3CyP( zg;>;;yeO7xAKw7^)1`QDigH3rBb9hS-e^x|<&}?{C=u8_mvsmlDSXhye`|{)3rJ-4 z!j6g_Lh?R=QC4}bfcCK{6Ui^`H34G#?QsV{z@Ba%LDp@v7enf{^lDwd7E}!6lZF|( zE0c1)(i$t8Hi;84D^S-C797UILuI(G@R)KAiVHLBspt=1^ouH&1XoFLog~ z*%+Kz8+&1I5B0~V{oa(sq}4dFKkTIsyFDG(PJU}K?MSY3lBI+-V$|ygN2Smf2>RV5 zO%)e}vK~Tv`KJ6*o=z#&O>VBvVha(cJwH}Sh_{WV{gsz@q~Jx8X8&O-$qxWGH?uI^ zW?(C!l(c!m_*6o&z_%yEP*)>!{lFv_`yt(uGKHyg^6n3(#uOI{zpx<-UmdK)2K3OU zJHa#YMSTE}vy=dJ)ep0@@URWuNZej!;5?(N9Sdh4UWyRB_|y;-wc()~Gsbu?nSRRc z8)LD6C49RwH_QcI5~aZiIO>z`c6mekktXCD=<>-3cB#wzTkh`wq&_6@9qiXye z>w0_K-%|NRhrdhbERlCTN_-*RJwGIV?GA4nPe#h+;N@|LJua=sb52EuxZwULsiY*w z1E_N|Z<7*?cLGq}VMlT2eC3K^&D^GuDRcz~g2)9_c|HL!@bP>Tt3=R8@$j$HWarr+!Kp!t&7 zj|p__?shS2x@hwi`=tTHRK54^(8bcF+$4V7UUyqDF?W{G%h7A!LnKljT~|yn-rL}6 ztNK~GHa;9->wzW}3N{0OoVQ)wgW+$~THVgC?$ZLXWodhDh2GflZOM-ohCha;)Q?ZG$cR_Hgc@*ttdfeR`1v*)3D%yOegvF;(%crQN!X3c4u*kSTn-OT4t^HL%DGKz znDz=gRIm0(@Hce}?$=JGnZ~s7%Fkn+|2yaZv; zd)u}(kIpS1$c`_{o8)dTVwIXHf!inWndP+gq7ytJ+}co&cbq{{Lfivq8#DrF$kv|( z1C6ff*7}=bPhU}e?%MHpm#en3?&TH98(ywFYVCa0`PPpBr^dE6h*Rs-_DxhkDp9!% z;S4H|!9vfoFC6tfFF_)Q{Yo*lz&PC|UfbAAwQ=Sql}GJy4K9aIgYpne zShHH=ZlniMY^CHz@C_}39iRs-S1*SO8_P{6aHyCY6M!a>z@S@$o(4;+-Cj7N0FL9cRf-%2Ty42B8+3q zR(SK_c1xHfSay>7f@hWb{(Y4My@4Vw2=P^_z&1otm-c2-A{M$?E@7|3Z z#SdMYHA(#$_g1sDOZ3zAQxr+oTLPTjo9WRQt?zzB*9JIf%IAHSBN>PmlW#}8tE=l25JO@_&NRNGn14;m%cNe# zFUZc)#Y2dReLm%6>!L+Sv+VN(xjA~!AL(|5P|+x?wEIbL6trI==PQm@l}RY7bGpKh|y&LWw7#x2}_o~AKD$8r!afHDwh+gj%7D&+rb+9$j|!d zgFW~+x6AT%NEE75wh|OOROI95}u35coy`CAfsd_ zOBxI_j%(6Gy@9vsG_gSxI{fukEKsd6Ug2arFQ^B(z#+X{<^flZw9a$7IRZ&hw(Y4Z zmQSn+xno;GtxNyp!c6T)SxH7AlwmE63g^->ST0}BboGBy^Pe>x1OsJJ|+ir|+xc`2}-Ni1sWsYsX?&PR0xzU6l*$je{G z_v9@mIc;U@Jd$H=U5{h5mbh2>2kbY%gr=?cOyO{^tr) z(|OjK>C1dWPVOhRk_$f@mGuwOzUbko`C6c1!ciLxhMiVZFeEdj*8}CfrF_;1VA45l z?k(sWsAX$v%&g>3a*0bqw;un=9y?&5;_?lIgd-Kyv!b1$JHF^D>Jqaaei_-K(li1NFb){^g3^jJd@zOOMLK5y3>ssz8${2v>x9?yo(#-^s@$j?@3TxH?Hb=o;_b zo?&|+T2jpT5^e1zgw<+8jsT)>m5Onpz+4unQz`xfS!kn0q65Kq{!O$vCe2o2L6f?1 zUa3t+DjBbkZm?f7mx9LfFN#_I2V|0ImDhdQYksj3HN8NN#z*%|MBg$0_CH8cfb!gR zfM4~6y3qgj0A4hS0<4ES!q1%QFP-U45K$9f3X_cgi&y}pWQm}G7pJ0%6mJ=4p8oGq zf$fS2&s-D7pyg_R0hCA|fU8W<^VxC#QlLp05#MS@a+kW#e`y-KB+$4aLwxlAcQ_4_ zI8{Eo@4u*(?NQK)Ig8dgC7E&lInV&HPnBVR3Zn7!(Q88-h9yKr!SkPMi*Ubm{yAor zG|D;#vE?&Y0#x%C)PJvsHW468gM#(u$$#5dsQJ|G=yDM|joLul8R?OHBo! zrI$$`gZ}Osr-eYgp=}>o_E~KRbtL8Ou=z}RI@dSA1z6Y`>HJ4(Vpv36&t_We*Dd>N zfb~(JqZDl#M2YwL&#|l@d+z{RF`dYc03`V}$sDG0UEk2Ohb^W{%`k{LD_?*79>30AdJ%Tw}gm-)lrd@j*Tn?+UCF-p?b>@{` zAGT-OnX4lr!$;L;>n;z|4{L<}kJ|k)VzN9kX{aeP5ER0kUmoz6+G#0Imuj)NY!{jQ zcbq=OWvGt2JbPNQ7JqGZ5OQ^biBUpKE~6u5qfXPN^M9tkKRfjLiA1|H?5bDjc{W=b zQrtEVn6T=ktMIwHx^mgCOOxOx$`4SbJvq55a~m3wD9=uT<%B ztyKThdekmrh@@RFL#_3PU-zzG3`yL~NG#sB#9&bHd(&`vMp&8Xo>|uXxG`c(I#~bu z1hW!wn@;sbQrNWLU8L^U&FDB!h$7GCybF!lPpPun7>cg}svJ`$VdIy7$7pZx<4OA| zTk{tre{9{f*j;E$A*(lJ=C?S!QRC)P4m@(<3ot#Ilw+-XG|J8ZY2>!^A;4eA-1n-S zq2b(f06R=_s#pc=a8}^|__CFWQH_3y^X`n(I*+@nTHaT`#h>!xve`B_%)3m@-THi* z@?yXHm0aniyXrplcsG+V?EpTB%PK_>aJ*gsSyKu@?0V;k_x&k_`^|QtRhK_%{gfjA zY}_l=wOKr3VSs2@ji$U+49akvlu>j%k%s~^dy}JTp<*2Xkv=WAc`nw^Tepk?-oG{w zdC;sJOH+@~^*SDG#Sd$*dAB^Yj%|7%3DBdJ`f~SByj>0!W=%#@i~*h(ktmZAj7Q&W zC4h+q+bOBos~%ve#RWL+2*jk6Z|FlPO=OmV8eP2)uyAhmP&%2<*O%dC z3eYFpj|rU0KgtfR5BD%TeHehAiC9lM1JH>KTK4NTqt9Vs-4nilpMFbfpV9!Z(wq~c zCXiz-Iy>U+LUK&nzPE9E`P7waFG|YmSR&VdkGIkMP64{N1suCE0kuR)4gg7Mn&9cO z9*m)#E>xm`-CZuH&tStt>NM5q*RdTRYweuK%bWAv`A2mC4I)5WUvTk+r( zU$j!|#pd$nv)mq<>s|s~2rz-01KcxH(;EP~bASVR_e2=zgLGtoRu)_UW3Fi*oh}DM zJP@-Ea4EC?Dq#FKN&wIDI=WA(8-x4aRL4?#&c399m3vlty&}M0$6Y#IA7=0-HZ^{fLb<0jsN3$5;5Vn(G0YNJ0d*FaBVhI1m?V%jskMl@rU*ZgF<%vIAIC z0NlH@w)1JtM^h#rt7%-}+obk{ztRsS0jiOL@(E&^t_joV%%<6?`rHdYx;dvq#X7-M zb(i_(1C?xe4=RZ6TN6{7rk#7<3G^08#|hQIA%&H}Jc zkI%lH(Sjn9Az-S6P`*RInHd9uiHcXL9w%?M88{eFmKwOMP9mkHII9mr7|VmU-erK^ z7Y~=W9PC_p1HSG3(wZ@QPk!&)V<{R$?}Mf-;+&6k|A`IZBGZVpk*bys%aPZhQk_)O zDT1>4_JoS6UdHLZhPu*^gP5K-1n&2{8plq>Oe^rDfFGU=h-C-qLZJ>@V@`MEEl#_e zH#~09&OmGP08-!XSs{1dTdlU4tDjo?&Mmdh8WD%SBJV{sNkGYn;>QG(dAgRp) ziBD=}&0cQQ-~j*hZGP7=-mS*kM@pY+AA}YE`s2{n+ny(`eMF{^N3SfvXuSn&rlF`C zWUT9eifIW9TO|+v*bGC`#y|M*yd( zadN%Ax@R)0939iL!~Ab4Mz7mlF_`|4fkpiHuP90^2Ag}%+%{DU`B}U_sXKn(y!93uxvI-<~ zmQK)`|G%800|#U_p~v_h%b5BAx%Rsmn5&m?T}*edix5`8D2DdBQ0&{YKDqx41ln|9 zAZ*OR&exM-?l*v3^Uur+{DeaQvL+hv-|nYG$oB)Xo^75n^ZdU7$0!`wHa!6(Pk+)B z;3oswqsG>6r^fg$>nAQDKx|0r$dQf!F5gUVeBALQ!}{agJ`i9U0?FMNUIiSsKP=s! zS9sGZ#4?f`0{8xkv}3kNwf+=q+EsN32Uwe1q2A4>X+8@S)zWH zz%2E&VAup_;q6{y)%$!0Jbic&v8qJdVa3-pErGdDbYwA zR@@8~7ii@Yt|G6Ox_1GXMYioK+i;MlTSIq~jr)@bOOZWSL7iVZs zPZPVX6pcxgMad?I6)kA}y zc6peCJ(eGqycEA&Fo#jCKXahLz9pk&OuRXk5VR4nUH3&}v1KKl{d{1l;A}>b^$_?Rwaoy&OJ(uxE2nthtr$ zl93K*6JsPkkU7A)2wnQ7uIu?r&~Dh&d4#PWa<)Bn*B`zFtd-g!pLGLpCqE7hAd~Zn zx?W}gY0q{Sn|Vi|y?!C;zj~a#S5n^mn&k@!Y?;WSt>vjGeCK$Hjg--M4l#`6J)8j+ zcdW7~syHndvHQG0!Oghs%{3a#jB4N(c(s+`UVe)MzNm`<5)B1~ypS;O^kT#KU4GRVY z!$rIy9_s}r?1QjsomRJ|n`lx`v|C+yNJLctOT( zod!ElZhu5qJd&XL_ZumtrA5$@(Wg!q0gY>-PeJKsv6uK#o&>Y>J3KMSOs+<3kw}|% zUHWL7qbGnkxHAMDbW#GYo2SX0=>!-+IJ!!$JQ0vQnOIW#HaUT=s}zg!;82!1+~;BL z`*n%eB-!cCbAz6&Jez=s9K1zVcLdD#ErqkkB=dk~h=v|0wGnMewiOS|VB8u02b_Qw zXnMU8wYpg&-=g#!83d0*|2! zUrv=p=nGV4N?c&HTRj>&TWo2_L8Q!mT;$sBz=9lJm9Ac56&y}PlCYzWcxKIMK{fm; z2Oy7`2bO~DKinTalmaP%%sl=GYa{6|lLwIYO-U=TBIuy1kv`oq2G2YwJhR zeMA@#O1@^7L-G->{0(4?41Sa0RSPP9)Q2{bnH~V)Yz}z0?mV#A0|TcM>WDLqQYz$m z13_a2uMBNUQid?z`X-c?O7s&Q53rd{mnP#4peziH8xtQzwkDM#+rTr_b4;u17DhQRG~L4a2x3BZ1awqi>ZEJQOc z$kBt5p=tdRn=<&MLcJnGP6~D`aYDGvAlDO5LT0n@^5tvBS-ZZPz)ziwAz7_JisqSc zAo#K|qPyub&@tMLNb^g@2ZM*+D4*9uNy!E1omW^bCux z2Of$FJOclY;}|N0;}}}@lkA7j6LQe0wU5UEpu7kzJ@Cik_r0!|#ZUA<_bijxfN^Hq znKTwXy;d*V?{640k*25DI zGTaY>w{Wzid)_9|X=n^aa2Q08U?XC!>LH~*W&WpM^A;UHc0dP4k(Sg1`UjajEImB^ zU@OJ+pF57%E_>LFhz>fTz14@}Lu|Pj_uM!FCZKPA8%V>?SfJNfUc!(*mq73&kj&@a zg;Tn&44(HL3S2Ed3)q&yE2a1Q8uO0JBcpd)usySt=a-h#i^l=XiL&BK9T#@ljh4T1 zkhXW6VvCdIAe2)D)HRA))X#7{`~$Io#f%m}Wp*0&gEiKXkUZ+>Y24kwRAk>~7tz}l zxN%^y2j1h7#Bxh8AIkGCGyj36EROeyQOVq3+k4;C(gP5GeX=5a7!P3< z>v;!2`iuTkcH=d-hzVIo+8g8yO2wjlBhbG72 zQ81am6Y}V28FZlojcDZ{+~Z`6kiw#;&#c}_Q)6=&-(s%xxPB#A&eRjUwx%-Bkb+%d z7*t|r^0R*|%o&lQI?Tx{s|J!vE)sZ_G|-K2`g(Ufm6mki9i2i-qk`7a(Ul&E1yvi* zzVGP$3UBAB9}2K&wmXoebNPWVt&8D(*Q}Pb2O)0~(m0Y6G)GoCDs&8R6K)}r>1!jg zP)hn`t8`g|K6^?Fsw^h6qr#J-3%=*d_~?&JB{g)o(ByDy5c<{lE2?j-EsG~J5jq~H z|4SXY8yV{FsNz%Y=s3O&djR#Yd-DE^)Q5R&5r5e4LSjyT_Z%+dsc(tfhcfN_-~v!f zk@y63V~T^FNWas0@|gFFe;SS#dhJ>gj3mJ5TG!*aR}n;&=jZ8wlN5w+ZL$*mOnoQj zF>EK_U6r4B=02Fjz4Ih!G7I91I6K{dtZuLOJ;gGnLuaKTZ5({&m6_ZZ>SKPZs5fpi z?%s~tq)GswSlL2AV&W#fZENhL`s^ch53Gcal%km&WqmEk?SJdF-9PNYNLpTq*~W;p zj3%7_yg$yG$Kx=hCZ6bxt)5OpQmPh^<11IAtZW~X(|nes02}oMV?!l|9q9q1nRxX& ztuskZq}NAK$oscovLj704?%&S>$XY$k(g9KCqO;Oazao8&Uhqy{mDS&9;)uN}6YDZ?K0y6>cK8hU5N;js z>oVQIMEYOYbjuEN_?-R}+@vV@5Vvw6h3Z=~R#J}##dfU2pQx;94gZYN9Ud51)iv^l zMUqTvN8~dYI@s1w97iNCuKU4y1m#Cy_z4foy*u6l`6l5)!VWvRH#lzlwoP?(zR-`h zJ@Du-eqW_O{tz=dcFmF%Ah##V_**nZ%>YO?N`oU9b7bWTLIJZ3GDUwuJLrDL$wW6x z{+y=sOsmp2hyMG)PsZq^mla0mvfsojZ}k?t2nKBDvG+k2xEeWJ^s?h_h?d{)R=W(? zlWabb!mI{Quo`9#yWJa^i)|Iu)PCAwCa6!?|745gi}>l|!+u&18g>T+`t^re$RcB^75f;^l5z7@kU^!*{z}!4Oz>c}j34gda+AGUm z%}Ee0N}=Kh;xg4nh0^0Kr`A~0W?P!5WND&2S&$70dX3sot7-+9A)m`^(EL;0E0?0i z<-O9fKNgvwpS%nm>AfjdOqHbE^|ik$D&}&QHIMA#Yl7C-pR17+SVC$G%xwM^ZrDCK zk5=>Rjl8E9Lq)vPyG7~GEQh1f{S~siI|shgJj2_;jC6r%Qn$O2x2l z*_rV1gcG?>kK?m%Y;;}5@81W&=sEcCV8J%A`EvE)ii0Cg$jJZ2?JkdN@^%f@toP>x zH1XWayiGCb5)2~+LRx+2xt_kHj*hh4rgaVY+(JJ0v|86kgnyOj<*X3}N@X746N{#S|5Q$&dM<)rk6& zKkM#s7_R*xU~=?v?PJXTkBNSO{c-)nR<>4nmX?K}PAXK#^xJixF%g)-|F`2xX%J`1KEz>ufw0--JCm+3*9iByt#ffYY9fBOYz~9Z?*!h9JpqK zXi2y%lPW;udl6Ysje+%JpdgwDct4qU2J@0wMl|@a4c3hOtHEl1T5zaXy+UWY!A6%} z_E`30LHrt7)qpH=vLOK-y*QKk)xE!q;(q z_MQzvzV4nsu`$SPUR+5#KeRAJheS z^C8ydx=v+LbD?a#zLE4LpB3=hxBUKHjt@|v)N~Ow;O3L;3O9wUE>vy%CAr#n^Y3F; zlynT$hP8#CB|nyEWq_1mr!Pezee>}V-;eq6xSkkU0D@8*xHU?m@f!x#%dRtEHseOR zjnV}ufA?S-2)+De?WmW3bD>hEHSl>4nhPaF@>_|oka2E-k72m}c;E@>8;ZXt+wrR8 z+ZBbYJyw0cT~F@#{bf*=)(Erb%D5-atZ1z@LJ!SdU#|d}A#Eb}!3bbw+Jvdf7s3zM zN-Y4uP4C#7t1CgHk0j%t#x_S^pd!^SR3xLH{(sEbazWP(%mTCf^;J(4I8BMo!fJtqyG=@c)mOzo-r7XGuDB<=3X)9b^U6> z5IB5QrZ%W$f5=FEaNM4F^22mLQleuyv?Ew{0buzQCK|dQV$mx{c{n2>Z+iW2J}EeH z8Fru$GIdw>lEwnqq5f9`IS@r!Q$F}0p;Mvw7sggELD}fVL6-niDG6!z+HW=aTFj8&0|6h_`KpjSoc(Ny zUPX6|BM8RVUxS~87$(`0wo1v0_NZt2GRL95i-O)1w>q&maK1!>^48#PE+(l zT)X?jpXfDBnGU7yno~JPn z;XzCR;~!+cUJel08sQ;Ze|Z}^eE`PF{!FDH_s=uG$^`gQ9YKzQ_&>)U;sN_a=c>r( zPk&I`h(Z9yzB}&8*ZXr!MFEiM1kv|$-u~tND1mz_{=YN8m?2Za_!2FPQ&)juzgwZ9 zM;fKZ>yB?Mk!>`1k|de_Pt`(22dWmQ_A>oq-K;H#en_$9L{zN9W|C2{d$rs|jX@}t zb#Blob;N4^uyx<=CXAx`fL8@S(gwJpoaRl_YRo~jayVD_nD ze33f^*1b6kp!I$E)!l6tIJwZXTigRJ0iRR@x=M+pYCv`aA{`U}KBxvZHu8r3$ zTG zO@tSaPoO#`@Pu0(Sn5}!pgDVAZ{7KU8xY{JN|ivt+ck+yxZt!-HVZK0T|lFKXyYQ1 z!h(y{0z?krj|u9hX?cxVk`>cwEcSybHf}bvi70%(2mzDD(~|+m7rTQA-*Kbe1SWZ`*`69E+Hoz+)UqZb%RkC1_h~P)8i);cy z_xZ=JB};yX0AY4~9t75t=_j`U3O{EW3f%i;+@1}j=9KqQbd z;;HEQ#Z)4<-5!U_=WXi~=hFw=b=v%m>Ed{eyX}Dv@;l>s@{hM&#==gHP#dQrXD6TD zhdrDSx`K(XuRQ`ve!FhclLj0#oA2qKkW`?-H<6DoISr!I5;pF^j*mO4U2wJ#Nah4? zx>^r53NgXt_nO`4``R&zH%1j?b{#ke?^n9R9edrck2QcIaejGQAE3u)y>Sjpn04Ty z%{$D%0EI?G-U1%}N#9S7()_=F^GpI5^GFLe7Brpm!ta#sn>xxz% zc*4z24}`C5PnYkry}9v=+Q42%7(@lYn;l!=HSk+aTLZz0qpHk ziU3IMvYga){Ln~c-voJt>bru}9xSEhqn~*k@|tEW7@GZy*9|ev=_fJ$G7uH1)!ts6 z|5`c69?TG41SB}d0l|j~z*OfxwV9a1cB(WRa&SBZ%e(#3Ihn&$YoNiF)Yo@hiD@?4qLWrfLokwuoGnIM|Pea=FeX_wJ zBfwF-fWEl;1qA1>;ZQ6gI^vVH`|Y!12YruAtKeGD18Wr0$0NFK$Ky&O7X$hH1E_hu z@RHg84t3)uXKx{RZ_k+kg&xMgw_*LdUh`)1>&@DrU8}@VBOTgO=sU#qr-Q9P&*m8= zNonDA_|dfbSD!F!h=%v}< z$ag6J9+-OQb>P!hW8*G(h$aeN7H?f3EI-u&xoZ{(`*7{Sxou`PhEf#tum;4F_25LH z&~`9<5GP%Zw4AB>j8_eJbPOM)G}w)FwV%UFYJpyOW&L46pa$UXn}gH=^mm465I$)D zy@Sp??{hG(tXNYh9R>U}KEE`I+T%YKE3?0wF@KaL@e#RQH$YV&l4s7=8vC&T8EpY3m_D4rEd4<_> zwS7qKGd)DI-FuInpz7^-!L!Jp`-|QXvkUg+>5e8Iwz>W9Hp)+LUr)Oe z+n56{PD=X8w%0yX2xmILSfcD}fX`*=hzA;7zz~T2`kipsc8YQftAaGNAfj4mBHE|j z5sM1ISl1YghI^7DlV5+U-^}&kdV!e&a>%4=X$IqOt_TzgO~5m;tVnsw5KQ$39Rl;% zo1AlgKx`4)7hnBqsC`OJY;EVjXf{`uZ&c;GS=56iXnPYwJ=|WP-yE&P*PGI`A+P{q zZJ;+tNXyv-)u~^Ej*v5ds7w(hDx}HsC9E8P73O>ysTly^$;cI;7#^hF>ZGPuY6<{8 zu@_%6V3?z?vL@A8A_CEM;xb9AyBs(?ZF`@I@--4g!gespJ?@Q{<`pMI%|IxFEkpZSii1 z?xSg?EBIVw(uW-eo1eCxmU0Oha51{YKCY z?rZ)cDS(?xn>~04(EXnR7BQWOUyHK7(utvB(dxCFA2?NFEO6bAz*)V!sirh`>(Sd>?y3dKZ{#3s3J*y31 z-$q|RG40?djDbl81V7mIq`v2uiO6JPdslUeUqL%flJXRl64%?1qo6CfRE8cDRSflr zne*D3Z};Op25RpLE7eMeS+9_{5;6N(DL{_CKj)V`X~xJkA%;l&5V-FS4Np+^{zy&A zZ$3gp7zFXzMo!=Y=58kf)M1HY;a>xsDeEJr@~bofL^%R&n@9K^M9;r%7?ZR5rMF=x z`iwLay?<;qM*|z3bL9MHn&Y zOkpZjrJi556ezsvxyZcnfV7li;rR%XB>iz4Ne|x-WH6rn?uAlWo&QNyVB zJKZ;;?7hwcMLTH<52IN6@;yW^@#$_&6_cjn==#sB5GI}%+XRq7kyhI6xA6+z`aS>KYOXa*s|FDV8uzyDw#Nh8*Q|zASoW0E+zn$vNQWonGz? z4t2DFt0;kw^*r2Kn-`M=?i2<~VntKaaS_Pq;L&b)Z|?dvAF0pmYuWRgDB8eqq*>MZ zegA!rvI>)5`eKtsns}PEUVS-VVs>YVN@{O0X7ewK^_pkmSoQa!rwh~zEdf?@Fw}~4 z5C`G)__XrJDpPU8}$OUa$u&` z2C$TFLUI#;7Fpqfjza^J`3Q)(&N(^(a~XhM=d@}pHw6OWX{nDZK|<{WSo3TuQbo?Zjy|SOw~VA3`^0MsW3q z?rc7V@}oBI#G*Oyxubqq6xQ07<-U0DP{u$?F2DYKj|VWbPh`vDB?-FJ0D!DN=d)&I zF1!PI`Nh=Mlc<}B+GKkzDb6Sn80bDr0 z9V({;aa$(H6W#;P+T80_ZVw<*`JFy_5_Jogw}JH8tjEzqL_d6ixOgIe8|Wx?u7`wg zT~8B}khg-)d@q4WqzEM9DNP9+kG-e?P;tFO`43{VWCMNo^PHs=srN2YH&FlZ*^7~5 zVReKwkDGe9Sk(bYcpiNXm+0F6$OW%i=1*CYmAp6F%y zHAQebtvSg{;F5rnhOL6S^#|-e3=$Gl*!s?!v`KJK7#m98AH%qUsHyhp{MS1FUC%oZ zGV+Hk4sa2GVKLyD&xy)Yu9SI~5NDmYi*>(%2)hQn7!<6IDfsf-VQwoe@Hzl;VgXtm zJQfr#MAGF@XG$GSchGF6q)UVhgXp%p78Bn$R4Jv~0N361(W*p=P+j<2iqVl(kP!t>>l!*`WfU-=4@2N3IQZpn8SV5O4&y zQ=`gBz$MGuJ~Yd_pqF#-;z;J{=9w~WV5a;yV%I56WA;ksAd- zN9 zZEg55119l!6*Ve4a7P_nfH;b}lZN)^>13V;a?U>xg?vk}+#O0Lx3fmBAXy?xq66NM zufzVTn3NDR}U0*FM)l4#|;7dL?$94|UsT!AX6hZ?YEVJH&WRCMfH zVpM*2u29;i9VW z@y{>1i=|@hcg}<;eQ=_QHeF{myDDb@G*}zz)^m@Q{#dL|^@l|Fx7}!5VHWL@h)Rh4~c4Q{|?+s`{aLX`;YQ|qM3!8h-5>GMK$ z7($-6+$`uV$(dd4ys%aK(QfVh3$bzC$G|$}=Jzr#iYOJPZ>@h_VSp{Bb|LLI5UjO3 zv0E)}GV6BPhgzFg*%~b8V?3)DUHpQ1e}sV!2Lrl>l6Jg3IpkCXi-3rMm<&Syhx@qK zNI00;?YKD*)(*zy2jzYn=s^;-?-nUi`42l`W#98~Y*MhoRjp%|7~5CY1YO2(MCJAL zR?8^7(8_(Emx5r2Z%?Qf8AwVWAtY2BWRFlpmRV-nGv3=L0hqQ81cBXB6e6Af>Ag=r zYNU&3pU~LdyS;YlN>J_D8mnzYcBiuoDtGBH(#^Kp0HqFQD{0A+LG=jZ`~+^83%yhQXgD#U?AaMV5t! z+~nu@>YByBF-{b0uepb_W7j-6J5de$AagxALbL@(uM0|#IFKA2fZx^)orQG_--Zf> z298kjrn&4rizk4H$;1ZFz(XW1rhLVVd_Et~1xYKT?ZwlBZE8u5Pg0ThU$j7r-rz7a``_NoJ#F6XmlN4zNjLTrK9F$Nw z^OWU+uMOAw2vhRxpqbWY#8;BSz(l|oXcDXNtl0Rs^0RbaLeG1Gtr1a1jKT#cP@TA! zML|3=*7N+O3UUJ@ecY)ZH4`1MS0SNblK{bM*`+oxM?VV4GzYOiyW;$zJ6`v(l?{p=k z>+H|4^54EIEGr2itC<|0X#91b$C2rHX;n0V>dfS4STO4wHi-y1JvnLw+R!@-q$~&v z8zgZ9b!>(@wi^-q;_AI8z2jC2mS0ZJH?Ylew@G(6&tXW%yCZp~#P+qkE{r&yD8BD8 zhKRiGlqO^0RSJutMEd42)}_9y7IvhU_jVjX$2c@h`G+?TcOPu-`sQ-~z&&c=1!3$&srbi{2Sag2Uaw5= z;H1L4TqD6!%5~dELddfU&Di;y?#ixTV42rQv74jx&dR5Cw0+XsJapJsq1H`LZ$E(` zszLW2eg?WLBX>BgK$6%_Ht z;K@cHvnBaX{DUM##yqY)lAB%p4$4Ar`!+-S!JsM97fzu?BcBAec@LEfdE7Fro_%cs ziO3xVA>?Ml?lB81Bk=T;Ny63B-mi@}qzZzb=w$KDzq#THSxPwt)ztkLz4o?8JZUCW z5fAQmB<*u147#^gL19*nar|7U{&zs_>U>F+gSKNoJ)PstiDO~S`*!M#zBopU zCwZkcpEz$``VzlxZ_-kT>O)~SeZStXtbgaig4FG3xWtBNIg%cE&bA%1q@DNLW8-6= zfUbgK%Zod!azibxDg;z22&fph2A>-@j|}$m>F!I?ZvJKsRiaQW1-Kr!gQ_dnVu176 zkP{tFv^qg)TH)hY3iTY@g~RD<7rn~fvuKqAct{(s2S$-LzK51&zo_$g+X_IC9X=~g zDy-IXU~;JozL$wREGrzjxbdZJO!N}8UBDgb@0f3@JEKuqfrP;=ttR9o+m0|AtD_3H zUy7JRBGj>Hv4<9#1FQ2@TX6$ru^L((K3Lwqm<;DQ-bT9|e25Y`8yuKMG2O@~ZXmeQ6eq{{nkC3=A zUH3@vv2Yp*w?(gqag-7&KZpR!-$Pae$)_eN2bocNR*8HdWConEezRL^g**isbifA8nQ-5Tg+;1%9^mMEoVN)Iu~NT2I@9eLx6Q&81>drKdyLz3 z=-SuHl@&x+1>+Aes(!0(3&Z(fGJnW6O7wDe$7^rMzS6(Yf$g41Ie(P}YMB12UVD}f+7tSnU zz3%+TnWNASm)=SdVEu4E?J0zBXRkx?5t}TdHXP0%tC_)XEm2y1=Wr1KcetB=D@qr45N;iozUqr$Y z*AdGfVigIGAxbEw^=%#O^n?F>4g6`2aA!CJ0?J9Jk^#804UjIIZ7Y&5QStwBQPPHp zL^?C@@bDwTy$nlYOHc;lrDo6sS?#_3?D|JL#V~;=7n{yJAuSjCmp_-37Uc6~Axn+_ zWm{er11{M*QpWKYf6xy57{Ks`krS!Q|2YRxdohNA;-9}#QZgVG5`q@ zM+AjGC+L>NM&{t{y(3GoT#Ti*+J%m0t?9Q%{8zB{cR73o{$ToefXARe_F<(Xh$kxg z9Y6$9KI+t1CJF*5(wf*N=lAMh@7n_Gg1f-2^Sjdr&q{om7Vvgf1Fm1nTDj(ly+5he zGn2j;(t^+e;1UO%d(fQtIcW7}K(x_Thw=VGvlR$=S5ioI`kDALSAlpvM*(>Po}&g& zAp_2bj%MX#-r2j06Ae^#T622JQR zDC`g{xS4-C&-09_$tNYXYiuuoCCEoZ(p@RWdN@-u za&uI-_Zg?AUVxE=1D_Nk8i6QvnuHbV{|Hh_l~wXSAVkpw#VuJ%+aCsMg*rq}C?KrhN~_k&a=%0`j!KaaT3 zAF14Mp2+VWsut=p zAPc`b5jLDSoUh-VaIl|c_wP&d--ZVCuZ3o1Cfye!-vZSlPK z?IDFL%#-<5nMhv@BQ?0_oTo{ary0T-0L?77JX^6)LZS zTVG)@$#Se$Z*P(aBKx4u&V3*_Zyc84dVUyKT3U)h#;3L>N%IEyXy0A}pLR4ZtsH~0 z#hW+zXhzf_HczkgiNT!H;IRB?2G}Z`fT*WPb``iAqR99iWNIv@vw_z+2pxB02_0jO0E2wF1%8aGv-WiY-sS78#|VB6+myVi%lrdS66 zn{i)4!VU1zGMghg(Yn^Ay>|?d+B*dc6KiX0w<=5{#xI6$azk1GsgKnDzCfe2q~tMN zwlihPt;Kl*($k%+5>1@(Uo9EzM>NkKZmjxk+<4=c$Fom+n12h84f2|Mo z0}foBLx&%t_x1M1g&5>_gVY0gTSY;~z2rl#<;HM#cn|@TU`ae^FkZ|~2m>5VjmLn5 z$2tZeM9;y7Gn(55)z`r8(*xEJ79ff!1UqHvh>3~q$r;U;n~&0YVy6KB?Fy9raz0dE zT<%v;THaxbIan6+BXEtn=pszLa19Ru8%f|(Q|#D`9a~qo$V<1i2(|yVW!J8IF;#Aq zBNIur<%PuhaIfn41d@Z*5r;?Gn6toGh!RfV1I=zDtHB6ND)zOFCda^PF68*;AS6dYvbr%3*GwyyV3{%)} zfIMMoV~fEut$U?Wpe7sZee(9eX9b2LB9mDP^Wv@anF$C~%_n5F`1)S+?0G9z$%mqG zpmrEjxtE-jeS#MPROpgxp~K*0=((3c6v}1NX$FNFrQEZA*C!ZDilUdTce;mJq<tY+QO zTPZ$e1%QD*>|1gP+voJ#ZGp|h^8q(wx4&n2PnY|IB?jKiwm72wpvM?)QLH8iI@!eT z70pKaQ3^6fKWt{T)p*<`4`VGuiJu%z@EoaY1Pr$lS!0nAS zMU#{E8lyFUJ$3PpGKRYZfMAQ=KYrIhQG#;Xy4y7=CUN)u@;U<4Q4GjKM(L7nvwe3L z0-jsxHQCnlH{%7de5XvU9v--Sgs(#=-atPuUb}--hp~uI(5a&hqSHl#sFN23-|`8U zROa5ac>6dXtSI*0+I?$5hiK{Kz2?1r_Uva*Mf{&3UrSj?~lpeswC ztF(U6H9OW-t!L6>D1%*bmttczhJ+XF7R65pQuM(4RoCq(5J?m!8R{fOJh&XzYXa^t zN|fGFnU9DZ+;`*A<(|+TWT8W{F;j)+uG?+tF|nu?bgj-J{ZDZW@$^_x8XXRn zC5fNDjd{qVCKLM0bJ1sMIV6~725!*mP2wp%+MCx#<&C)q@od88f`w4|+zY=a;6*eS zMnR(^VOMSqB7862zMY4v9Ym-`tvknK;EsBaBjX5q4r-?&2+f z38c=hQ<1gnrJ*n)P19lBb9cay4;-Y1HHFtDN*{>_tHqc<)zM(%#$8!BAe+1$inxDC zsb&E6Prr|@bL;Br8l%!xjYs|}q`6~N_KHF(2^1sw);DUVH;}uyJ!ObGObpct5=2O> zRzSe>QZE<&B~RY}^)P})Tl_%7@wH&qYA_|0^h*tAF9!cZPO;*#+VlqZ2``LI_@ z-|-??vf4Ee5ehkP9gvs4zZBT|lg+1Pn{j#&^gE_U>>TL==|2 zI!@`7uqLDe>=i75s03QngJ24TF;9M1GrtotXoeMb5$ucM70u2?_4Xie9*|#hFZ(mV z?}B$8qOc{5k9sxuI~yXYX)s+*8Uy;yK~@p5A%#HHnP(+Wz&lruMcocWM5_%}f%=&u z9;T`A{hXWONpsw{)P2eY89E_PyiXr(;~OSF+8Vuz)_O2nk9-&7tF3Vz>ltWcBs{oLCk>io>`p~rNbJCV1{u1WB*Z3`FLvt7dRitvQB5%C1z6>bI- zH@&Qjfb5iDE~7gBfXTI>``Wn-3H%D-E0UyZQghZ`Z|6s(X8BnMZGY!i?$GY{Alh9j z0GUw!h`1Y4#1@B5XuRqvjcf3x3i;lgomr`%L4#`*v9J=h+#A9;8MzH<(ORrbe8P7= zo7jH7bz{N~4VK%DmE3e=tQPK;)W&|;PbK);tja(=VLq&}+Ih##wq=SHh0|wi2h)7% z*w68v7|~`Vmhg%5{m1UV>c)e6dCm(ksc4biy_raD}v3QCMlcQg(CY2$%ua{8pbed6yN}H|xa*;|h${JPgL(!jJmo1y-ld;I ziwcDp=roKN*tdxYt*Ve03`yI4=N3q-*~{HCFf5!FQE`G{^(TF_)oBwaEy2Ou%Va-@ zLu7rYPw}r?B}jZwBgqYgei2gcRd_4pc3GbGuN!s)tDonIlS}WEQ8@4~ z*Jz!fnJ+$VQ{}k0#E zZ@^kG?t2Xjg^6~zKVRQUQK-yzk$za8U6pbzb%wiP#XZyfHjChk2<~$3EEd~Vl{-hn zk$Eze-Xad~#nWe#z&uzrxDc%sUwQSpp2F}EkK7EKD+DsSVzZ>#`D((2cqm@wTtEr| z)Mhjh&rq3Poi*1DhzG)`x<*<#{T&@~qIqLsC^P6nJ`y;3k{hIkc)=L$;(?wkz3ed- z`rL=&1H8B)*do}0VPTrvzC?;82@>wQ?y5eCmE4)6X4h@y*$LLthSwdocVW`nQ6r{f zn94LW>oN+jasLsVoC$Wj)5K#=~W#;K0XyY$c(&$ z8u9zbm>Gcu-N}p@!}7`hj=+z%G~=eHH)+@pNdNwje?BT5MMdQ>_@Dp&--lBnJd9y% z>mK;u$$lSw7<2XDpU?j1-|T=pNG3rrcFw;({|*xE*L#2e5hIm|Xd;#CfWlwr6A|s* z|96=1j}i$21IR_8;Eew3C>Rp||KpJKB!2(Oo&=?K8ui{7X$>ttYR5ovL{LsS11{xV zM=BAl9R3u{8sFa!>+eUFgYqs~9|m+}${75@|9))g5zHSQ*37@6FBhF(xv=3}5X+P_ zG#WDtx5w+K920RgY_IwIo6~rTd;%t@%B%r8Cm*SxCjpJn*;TfA5A7 zj!b2Qqh$|AWWJ!?`%J@50&|?`ae6c6xBus!e3Yg9CJsf(a%;`okyfs%VeEE&JU!4T#bz*^OzPEYck_s>$p2Z3)I4K0Rbq#vf=OsC5i#O(F1Dq&#q6jsv^Kh z^REGV6$@mzq99YQGz{tf{XQT%7%v~rlI{nga>gJOOSM3a5o{OTurpUb%8b0`>+fG! zbtV312w<~}ek9u0&IIHnr%Ze=1rAT~dac#A*BEQGr8f9|R~W9ZuT20TP+_+sbq+-B zYdw+Fsz6>u`v{ASFME*IhgQN6>>IoWf?QOyq%aeC?J|9Qd}KiHlnruKybj`dY!+gG zmtMBj3uz7X((s(RUZBnyW6yR11*tJm$y9)d9yl%nQY>_Gq@VSev0AFH{XNFSWH81Y zvxSr8=g^PmQB&J!pSwdD4j5p@LG)`-7ngo;@->i-5(V5kD0G{Mh=>fzE5P}vt_$LG z)D3Y~n0YLxD8UAB>dV;3+BKGpus43bzJcFNUvvW?h#rX0V}YzNsT%-DS#+1_7`yWl zH~IMt>J$Q*K>y#y0+5D(-!fxH1al6*5t>IIL@f=3gQe@(6F6-8I%)cvUC%295;@mP z*kj<0Dy~1gXMpbwatL;SK<}VOq!kFk4w*p%wLcFn7{gD8Q~@V7^5%Pb3JQImq5@() zVlr(&e#L?%coZ@7lLEYEr!*?O@p5;aS5bFI&7nQ7@;Icy5`_ z<#ri^I3J*mO8xAcbDW7CNZzcft-{?Dd4W$#TGSLW!BL(s@f%&w(X~ zr&BY{tY8V0z*I+D+=fm+ph0qd8U>fq6GNGD7m>CpIzBf*dhp9TQ0PD%nbLEl8a0e*y7Z6d93GD zAV(fI=hB9uv=L8fb#+pyXq3^L%63UAO!)0D%N}zHUyIq5P z&j^qqs6nX|X$D(teGB;z&|=*H{T78)k%| z-v4F+c-n1#p?LL*JCuRNLZ6k6zdJVOvmm-mKqSk$M@g51pBdLh5}N8)869W!x&!*g zJpZdBF`Q@omC1-hOplwBkT%4(=AXO0AZMl22s!FO-+nQqkr`4^NO*)>WwbKeIesJd zOmJ_G*Y?EI2iB;Y&mUXQoo-tptX>^eRNQz+ z+4n;8;48F@VK2$wgB{gI4V$~&;>&Rf_Z$a+0M=kPgk5kv^#$9TG7$MdDh z4G6;@Z1MCcc2kZC6oDdsJ2OUhF6-OaH>jL1lNcxxs9S5exeh(f)C`CZb|q0=PwFWB z4wBhTpKGVsOYvTBJlQ-Res=Eq?ZW{l;ym?jV6}A8S^oret?~9bLyAtbW5F_Y%AY^?^b&tnnKj*C7o$iE*5=(7}R#_P} z&hTD%?-BlF$1HZ8{uk{05kgX^j{7_=9kt6tl{JjrIDJzVtVjf@AAC)Ybb9>fp713# zN{mvGZf(js4S&dJm0K_RlJ~Wq zhClpA+~BdB$>xprNMFKhuB+`}$JqIr61%G(D(9x!34ApdQ$ZQ61K11$cb*k^>- z=yD%zxIe3oXfv&!a&*9~oC-qOQHN1nl=a+qOAhP2;oljTI)ct?r2VV4;qIaxyjZa% z_Ozn6F07z<+CkR!=&j2bjLMWpZS5p|w$D58`sOy&8-uMWWTxcN;};@+d%Wb3NU40+ zU`UIlt<#NJ_n`DAL)^xnk3NL#YsfUHW5wzKp4cV`&w)>H;n$^E^lYSK-**7hkN9k9 zHwmew?#E~}>^6L!5txK=&1fqbDg68vz;Cj0W^&Xz^?{jJ?meZC@)Y$C7_2Xb636MttpTaw5ry9ihmCuLMQ;SmF;~MQGAdRaD;M z!e`N*F+^vj#`KPPWH+?ln!ubjP9p(>3<15vcE7p^PO+uRIA58s6Hcp(^d>ZCGbwCm zl19mE-NJgY8jWkw=v_WZlBM{uLAQ#1c8E{fV1_qkH-2i|Z3C5)3kLyOqbwW}TYKp! znru}GpD-%dU{5{}EpugD3=*Zb3p@JD3mR1nX%N^HR_{Qhh^~B=0wo`w4n8J5o8NOH2= z*3V%z{M9pmck#- ze|{Dbz%iDOeN_MJJwXdV`4}fXeL&7E$&L2EQ`{xQUgZ1t_x4imPz*Wl?<7RSPd5^L zM=)knGWGb+7)9tH;OomO(KL$wdmgZ;OM*c*_f+cr>9cxh5E&RadjpOC?vk3}%^#uD z#9ysMq7UdS>7GUI{MG7Y6N0|&h-%3BuYpHngMrh~N6r7&$x(V9!5dD!RZZ5vt^+Rk z&WWoM6(oPILxdg}%4o2__Mh+UsUQYM9GxuV@O$q4r;8g22Icx1XhhaP97rsvu6se< zPs_~AtbPHvM#urD!YZJXKC6Xr8AJA>;V z@UdSYMpRT(P656bmHn@uinR9uQx5@LzigF7n(JX;(W~+{1wvwCMj)AQ0{*?OIiB|$ zDztRL4;c;jR{pxE5h62qwu$Lux}Y8nKRY|i4+k_%9H5Oq?}CCt_V(twv%Ov1)y-`U zFq$tf=@k+v;re>8=aEVf!4oF%!V^Ggu_%O!1X6S4Vxy>~!+L;CP`!tQyl*s58U2J6 zw0e)Y-lX?|%u$}$2+0hTN-SayiCruVj2IwSQC#zl$Gfj=wmnfiegJww#*BgoFswf} zEKsW?TW=g2_m>tZ6<8c|TxxnObW>EBCaf0^ArmF5r6<2R#Wh zxOlOEvy%aQ_ZqmW@xcBd8%{2mZMV|3yuV+1NX~03l9`o-v~&zQS=my(=2fu0Srp*T zdf)^r`0D~pKmcS8qzY>1DkM@Np*@TM0y{BJQGPFP?;Jpe_uWHd4?am(_^X$S5E((K zQ&9QY6Dh9Pyllx>JY=8uyZxqP)v6!&v1>ym2u5M_5xZ2ABg}si& zBp(Hi{m;9zwKj#00b^jJMp+;gA;bUX_UADdK~O@e<62GOG*c4YI0uRQefLmA1Brit zoqkon0Zu&_%@G9rK6eWX3gSp2CLt;8(ltarq3^VKRrHEImcr|Kc=6czdor2VWZi*m z0(1{R%8wFus=qY85x4o@XOX2|EZ;6R`T zFL^K^mf54{2gH7M@u-RF5nJ#1SjA>%*q-&gy@&iD2K4{Ep!+oe2{;w5=l0tM)_(&+ zG$Jh~Ubqko=xrQ}ZUNmK@PtS`y(fS5dKm*_LB!_^1O_tU1&5c`f$XLa*mvPJWUoL@ zlr(CZYw#5G0}luzA*-I+dGui(FdH(>h0%tvy(2ZSBioa@NskugFzUjnPBl3{TvLy1 zAd_WGGyi7>fd5Nb2!BnEe*Jn;*~ss(!;oO>%^s0JG+J$;u~vQsHenp6Aq^=m(y6Z? z<$Es!%%3HL$aFg`e2#lXbURQdr(WQrDWqF?&HfAM3<_5k78bU2Z->E!aIz&W1%`;1 zs3QPS`5Y(&+&nKe%er#HYr_RVSkl`=tSvII74O?DmgwIV#=rA8D&9Y^tHw|&YMxx{ z^N@In4L*$$9miNNTP{;Av;MfY?d#{lXjda$5*kTRX(N(CpDJeyK zr-mOu#dakNybbGu5jha=hUiZ8zt3m|Yr1^d{DtSejEoE>w2vf$|32k3Qm(`OGgUZM3d?N6ee2KS z{eQ+EhFYhi!dJp=bqZ1}6ajEy_L9$TSpxs3UqtqQ5GGhFvqMDUuwxPg!wAJThu7uTyWR?i1 zXfX^n@gCdP2}|ZAxk08#|NO2D%iT%zz74i=GOnh`X{Eq#v)GZM%0W9;CYI8Hs{#14 zOJAro9O-K34@UNxHf>d{G*vtYQ@crb7}*Heu(RH7#g7Tg zi;k%eyTsfQyK;DV*j&frLc?qqAfwfIRw}+|_%xs*2+TD?`0s0<(k-qX{&YGI{?<=N z?l<|}v_}K0oLGG(#3R-3JJ&*UW+$~{tjpUj_|h>`Wp2MU#>fa(N~RTHJ;bxIvGGL4 z$VHM@PzW*Zk5k-z*GZxKB|d&TvLW;Ov!*Ff4T1HIzm@9UY7d0dbU=m;6YIZs!WaXg z)%)qq}z@(36SS@?!P?S#~ZZ0_dfW&*68Ojtf|z*=k!l!se&1X5qG4uj=_ zkARc7gz;6RJJ`mxz%>Twsz}wtvV`B5fj{_WPr&@@HJy0C6=2o5!mmNX*;Yd3HAoqY z1&SfH9BE;Py$}-a^jU9*W#5cDY^2D@JME2G5m%rPSpx~@F>ocLWj8S16&$W$ZTU#Q zioXW(r%$8<5?x(DE4l33M0R|4VE~wm5+F8JF7WDNx?!Gya9w5c83-JCn%$~JQTty3 zHWDu~Q#Ts)#i))BGt$v**`xVs+xPFTLVgL%7SM{OtGz%mu@pe!gu>E++%d@83(bwAUv$2>YQpy+ zZ*nAh_X>ok<7cvJRpr0m9FYWCQF>Sdm5w`**3r6??fMhB09&4#F5DRj@+xG()uIPt zuy`PS(*}_})S%ReEh}TAt%-!^d}=JrfjPP#ycw|~l{O51Km+ChK{->0l%na|I< zj8pg#*!Dz5Zrd!7ME&iev4gS6wW`Ktys^JC&nG(4%-K*gx$wGJTk zzW)3B5CL5uF~N4*<7q%3$G;|gr3hhPr{97q*Nu_6@I@F0Zgu1&L>-psi~=b zBq-&+Zg0{ae1(@+)ndwFtx(nMc4H#5i5e?yCvcu|*qv+_5b!iEL61RQ2R-=zk@p_% zT=s9=c%&j^?~;$bB81G0%AVPT5+W-jdke`bdzY2H_g>i}dyA~>Q3&BVKYj1Ua~#iK z@cVUibR4(a#r3(y`+c6T^EHxX<8Lc*h`Bi^HBu((8DE@R&%nX0oS<^yutm|8n!*K+ zC`*5xVHdHTuDH1PFrOy_G5hbGIn=`$&&suZazZaDi~4=*?N$u~>ie=f8WT=GbjuMg zREkv6R8~}v^|d53aaxnWw96m%{?5KaLJ#C-*6E6LA}H?*V(|CQkF(KGdaIxV4poV^(F?6D9oz zIMce&1vBmi5WWFF|Lh_}4nj*w0-A^j{d-^C!C|VC3lpUZh@;QAcghgtxk&Nw9-f`o zjc60^cG>Ruy~bD^*GA06lsmY!@Fb}kJVNB)X+eEnwMsA{O7nX)wqi#$VPP}~#NZnZ z>flsr-4!jHV%n9TF!1?wSbuhWr&POdw@l%@D7rTT&YY=F*<62dfWWcfwuV^2jfYh# zY4@CJ>W)@f=v=6La^~^!^{TlN2epXNwqU)^Z(-SOuJ+sJ!XgN`DSoZ%nr6>9NX}(0 z*MyMTXH0tA`@EW)Lz-Zz+$KvD8hT|t$9>P~`}w)&5?xI1vAz=}KcEmS)rYQujL563 zjDw$kCuY6(=v`Nv^GuE7u!PTX?ujRr?>S`l$pXA_oMvM;O<=hYoEfpM_=UD0H{eiV zv`t38l8o!R9Z%Yze8w+_+b`b)|(d#=Px?}IF^fl65)z+tr+ z$-vJxM!4$G13aMY1Pde5$UiF#cSR-qjqidlhH$I-@zI%e6pMry{jZC<{Y-dnIgT^Hoo*-ba7N$#ObauFW zosM-b?WF|Kmw}m2t&sUb|)fdUcWu8N8^xFx?*zl7eFaXYBcP1K{5AB4v)QxTZ#}oz#jWgMcHP{*{&j4mo=f6R6jcB_j!C%6<%mycb%M8N5g|LC^D_FL#a_}(x4$E*-R z4QTLs`B`S7XZL;tU68SZo)BZ%nsZBv(yG4{Atw8Dt%mhCKA8{t&j%|Ziu?& zYSI3b)+!%ucDvydi)`pn*n_RooP`DN;`{D?*Z4T!J|G@U$MGzw-yNc4qU<3dJAW+! zGxED=qt@I>GcCCj%HcyQ&mo$3*ONV-_e4+msxsZo#JThCX3kE4!O=^OPe@P$8S!C; z=9@QrTaB;5N38Z1IW-RHa-b@%;+4)eQ~b{^--0wLcjq2qsSTPpSzW6QvJIXv?rGcT zmO1x^fwk6eO9adjQX%8X6g}otSL<^j;Z>~WW1xm(Qx+V=H^j0vQXnjW7N@*Z_i*m%{61M)shif-4 z1#+}G7>CD1(+$m6I~M^RYYq2k|B?XT~{Z!+fyn^!*V?4(ZfYRSTeWHz*&|?w6(}x>Xi{6 zOUUtHh{%L9<6wS%PRuX-Fz6f_l>LtaIo)n9_rw{OT?jkB)57`_-Qe*g7+kPA6cMXA zH{je#97$u?iHQjeLJ|s3Dhx7jf(6#cF8+O@ux&*)`f55Oj8gW(8o|UJ45}9FV`LK^ z$}p8WYlJpqb&}A8CgsxlDHTb~cRjG8@%d2v=-w6C_?G7`NjnM6Wr?(FfuV^{VB_E7 zPc-sl=R@hE&KQafwAv<;?M49 zwN*Qf2RM=OThXODK6PNILtyvKXG_S}NuQej#yk>)byyC(K@Zb|2DF=VC^A->LcHke z%@i04v{(e6sn`m!$VXnjG)6b*%JEI>yy=Ky$j7xHQK^-|FJnBtmEk%vKh+zLw*8PC zh{i|4;Ls&5qJsQQ) z3(@iXzyH|zNRYpd{r-85^J|RTurlWeLe$6V`->1o#}Oumt6y}XDQ173z31+kIqi_} zammm4L-dAbe59xfgzz$-1SB)m3pmDrJsa=Rce0 zFkqqlnl!&MLbvCA`bh-B&~oegju5o%GF`WksB&-RV;QK@Id7r_+P92x3Q`lzGuma~ zK7{c*`xsJ>p+P`s(6OBow0C!hmPvxgI5g8INwYY`c1n(%!uQ4$b;J{b zBAZ&Sbv-t3p{_hjtg3Z_}@Y%@g*l;_DmC>r?`(A z6LOA(r_W`1NHc^Abz2-~3nvI|GvXX&2c{*T;KWg;@qt!kv5Us^IbDbBxnoRGOvKt# zNbu9LsAlji(Hol*;ww=7RWiKC&^JK@bYQt&o=)CD7dJ2}XX(+EO=fd(4=cj=WSRU} z^g!Mt%v!lOl;Dk2qFeB4u(}?NJO1fS z@g@1-YvEi^F#haMw4rG*{xVt_?Cbpt(1>)x*V5w=GJpDhDo<89fZ1h|++HO91rC(u zsd^}DD-d6K$6YrS_?#SH6ZW_QMWKVOP+8^jF?ZCZ&ic2zi3MnX&w$F)7BOor>d5yq z-lc3umCxBADa|_(CgUuL*7x(Ke##S@pJ~QM#blTI%fhrqk@L%rbb^zGDZA{iH3HWT{$3*(*IY-5a$+9QlP=k$@E{@QeM!()&CcyQWVpag>|_!Ul&act1_Nd!aJ&mLqKecTczAdN70(S~ zcr6oEAQ^?uu0Rpr>i+ClroORXMbfY9*MN=Tqk}^bg&`dnBWR3a!^_$)=QQLdXKhqZ zvoA)&*0Ji%9^lvZ=;fuv&iW8H+Mdq(*e5z*&iwWzCW$mi8AouxSq@)o$A)ylX+bYQ8P2^>G2(OBSGm;JR;l4OHzCY8KWP zuVmH`?w7y0vEqeq2X>_QV7p4M35PefE|8pC+Hq7+XkOy``Sa%u_t(d(G_QPymhF8_ z*cF`gZ}R9Ja6qOyKkv)oFp(v?a~y}KaWUoYWTnQIA$XZuTq$LV8}y#C3RHDpq(5#_ z*4A{eVn4Fjh(XR;3?Z1_8p#pAn#@_PZpW4D7td85R)o?|dhJ^7TQM_k^9dD@ZnYQ{ zcYt006Nr#v_BjFn#*WMfA5!HL_))hgSOo=jAWcoKS$y@q?wz08-c(bQAfV&3Uht>- zISo!vbYd*3S*Zge1!7|++BFV^G423uXXU+nN98)^B9X{p&>C&x2X0G|=PBDK>)qY$ zenOOH#Iw;&ETXYWn-3yb&Hx=lTfz>=xavxG)7hkmbFLk$W>Jf5(WB;zsuzy}>fUXE zh$JfSVRfvzR)M}}%uJQ1^&G}Xe9ofQ;PYRz$zK5WGkgHBYlvK%X=4W`KTomg);I_j z`kaqIU6tYJ!(UZx%8riVVWKE3EHrW;Pe!J!e5s> ztg?LrG6^JyX%Pk{NK&5aA`Ge)03MLeNWA@AjOtV^M+X6nr?+@C_uJvis>tL;NiaCQ z@OT3naW9Tom~Vwq@NRy8cz_~C)N}x;fvcrXd!}?)&wm1TzuQV-*^VqDjC{bQ*z7!q>|^qzvs|&cziiOs9P9_$;TH| z5#f=C@U(OT(<%G|)+J^~h{J8Q@Rtdzy^@$l@A6cf-{>2<>X{Gmux#|;LDzvhZda&Zx# zeEhvdzc+@hw$DbqRJ>n}6t}$9|4gFif&gdI+>&Rb@*7}xU+ToJo)Tp-gduQZ?b_^{D< z>AEfdr1pp5x#uU9pcOVHpp@+<&euJWub?DwL`6ll9dOv%7Nj1EhlYsm@#aPPXWu_Lo8|zB}&*VyHh}_q7o|J+;g8k#1%V%Yp?D&uqm+*C`8|Eth>EO_LzX( z;r6$Mj+7%_`%Rtat;3HHQ-3A^p_v%K_0}x@>RrU(kmhB-w`8JO@{PtZXFuDNmF_Mb z7Bv^o51XA=0pA$25>Rr7w3W~FJE(ljL7W;p#L9f_E*UVfyeAcAq%z(iL6SZOG@?$C zXl?df@0d|PoVFf~a`t(c3ae3*&qxgU6~=8-@^k9x=`D8nj5R-@@mVZI;52;;Hp*`H zMz^FzJBEmeVFwxiI}V7sH^UouHF3X?hE$pkGkdn-Uqu!W*&LtYvuBrau)cfJ@pj40 z0=8_LPs=8F-M7*!BhS&_>p5vEwJWfNidBUH25^pgEAmDAU+tF$MB^0LBWcvjBy$_o zzJ$?~ueL8exD#+!FRHwgJA7S9yQMk*wZoEm(z-l-Nlg$}zi*o7O{NQo9fTJ%sy^vZ)j4<^}QOlHg8@(-Fm<!9^La!otF)W^`iH@NR_x+Sdtfl41Jj zVywXFq=zvsVh$1I~aR9+Zpfi8vmnlMIT}w^@pF&g76%r0uNX-{ zvG-O2ITGw49b2H8m*NxZE)3sR$rBP@&Li%v)(|-&&S4vuu5lkNY2qGi&X!LU)Ux?B@TOaGqQgMS8X)03nr}Dp@N;Tu9WXO ztZoYm20c9hbLkWxhE7L_$o}$5W!myP$^2c!Q@lLC#%BR(u}}XNQ0Gu7zb*BZ?{Nxh z3W5?N82MrFNQdCGo?Xhh8dZAFs3$lAay>(Ms?Z|&GmaW z%w+=8d`R%;`4S>7S-vke2<|N3Y&M_^dyJ80V~drju@NMf<03>MTxcsO|GN|oEk#bQ z&F{V9h;T5Z7ynuVIFnHnZfmPe8Nub|xL1|`a7|)-L}D>IWt&)Zc~?jCw`|j2)cE}T zJu8I^924zfOutD{{Xf~q|NcklMI-~{9B1}FrD?Y;l%`xs5BGzhc$vnS3iOX4F+!8E z4V257%$zP@O!-MnBcE0=w)2{;E2+BqfzB1ob_cYARu0OIY`qWi(~}1n&nWD_eriN` z_yqHHZ-180c^SwZYO*N}R3)|s5b}k=T;{0|L{RAfSzD~SiQDS1_ zp%qlKxX14npdNr}jA9`-3zmNoK%~gFdkrWk^P4ws4yJU&=ezOwmsoKq?|P}KK82qg zW<7NaE*z(JE@{&rB{)NP@joSf$HXYn{mnlOXydR6~^ zYDveV#;KJTXh#QI?2fw&&&SwuQUdp43(*pX<2NM81(!&4r37VLVjcw<`QMX}8GPyE zfzkkyN|fWp>N&{?7cv(J!M62m7_9vC6U+S=jA@Cb!VXKh!^rSQKb7+&YwW zJn}r}Sgno{vA#D>@?`B5dWTWQL^_enLc+|2;ZyGqG^qy+%W3cGKinmyE>YYxOIE)q zY{e{=cr%!&cf+LQVX6n>_HikC=>Fcx!dRu`%E5|b$JR1aop2&&jka5+F8^s*$JNr1 zV@_fee^EhC><~r(bc<&_F_0U!Ab9;-Z%lAIg7ISb(-vJrvc06^} z2;hqSH^LPeq1tYHKWS|yO}03<#xa*u!@X7XQ#jd&wEuY~=Ymho7+pTTwO(#DLLxnq zJF$Qcl|6+^fPk3A(Y3h4@;=%2Q{nA>DY79%J|TKL{TY|fY=xU}R^XAi_ml1B!*B`f z&V0nKCxU-+_*>W~?Ly^~2!h=OZ z6}cjl-|>AJHGwoz6F*`w+p3Tq-=_(pRh&+%qc(oB}CsC77P_See01rd@?iY^cu}u@fT1)Vb=d@ms z6crY%%?9@g~caafF@SwQO&b|M9+O`^&Q3a;_y?~$*5L)b=CT- zZ0Zp68Vz^AVrV0mx1r*(U^>b6N6kjBo#28;)4NiO_%mOO7hD(WDz^E>mabu2F6lWp zr5&GC(&g*%94;)g6m8k2>Rbs?G910JMdN<0j_$!lVztE|6o}ot(s{8k^q@2It8x5{ zo1|j9M`Je(lKYiYro4Q4ZH9wya@czQG?HD2G4)xPLrF8#L4}ZrBC_2Pg%`~aMnIX9 z#;*!hHrcJGp%bm}?v*` zi3#i_`EPBjZ-_s_p_sob^v>UeEcRSY&(7O@ePWQ z>9_YItULEp$`?`QPfkrMu|h)SA~#;g-N4K@Ol!F6wG&@3o87+~Hs|OQ`mylx%i1T+ zp4B0TWQpC!or~cGlN_k*#<`p`%Ae4)*Ir~a{a_d*7z0|L?dp)!%Vy9*=+U9seQ0iO zmh*h|JW6sB+%H`H94cmb?xcgFgQd#9eK*8ZO5rA_LY%f(LyvxnKBN`l59PQ}U8_ z6_ekVUuR)VA<@Oxw7z%EIh8*0PC)jlAfIGKQLNQ%!AN5|`uM?10cGYaPAYNg7U3uAx#tP*4x&uNoF6hcQ2$nXk zVQeiy+rj|W{tw!K6Oc#$1&dNpBd9V*IV~Xe#2h4T$JMVVXUtI@~F(To*A@fXYZTIfTN%U6&Q`s*a^J}-CzU<&9)zfT? z4(GDB!n3)`(87tvUK5jPl-Ye}c;chiRs4n)a-WQAHx9kAqECWumR*SvU`C@RFkAre zbmoyyE#7LG{KBg!kLy#u$cYE0P`HH+{}?XZzHUkVnTfT58q+*N`&@mI0rpWRIhkYW zgpEEU{~^ITd(2@&?dDY3xpjPf^qz5+d?}G$Gv&0@a!poW+4QXFTQW=c?;~bcR#sGG zlX3Q``<3dcc&?>2lS&6jNwiGa=ndp6ZjQVC@A03CZSdx{mJ%|!Xn(&%(%|RPf}R-d z52x@UrbU`NST~3*Q5Ej56HI~%bCw)dhP=5>WhA9R1h0afs3vUXWzlPnl&k-nC)3`i zlDn{2`7GP8Z(M+7ag>oqK48ftL4(3E{&$-UM>Ygufbg)T=$wX0{ard<`o{FX?5T4Y zQv@hc7)XF14!dbRH;h3+I^V6;=Jve87tTSef7rE>R0c>~qUG_23Gg>cNo-Zjl11Tj zxwj>kV9y#E5OK0I>mZ0~dayZdihYAsT(RV-QsbA5VAiisLgV=zJ9RqGbrIaMn9^`m zE;Ky@hQjawQ%@Xk6xd(C&bOSJZ@(Jx@gFi|XaojOG__t{9cgL~_<_J%dyv4Lh&X}> z3ntN55&}Q~l@+>6*+=w|-v#N@>;epjC9&Kp6oIlWTtRk1oOnM3Z9PojOCejsd@uNp z!>w>#e>+%x@yWG)=ZtONb12_o2~h6~7XtQxR{Y^BhQ!cV?k(L=2iWhH#5e7*HNKew>~&;Na{#c@F3R4H z|2Ehu{U(P?POzG}?XPQ&mzac=tP@64eFGRq8aPL38img8$t}a#>K9lJ#C0Npy)bKD zGWYz7)GeRo9e)O&nq1$u`yjlOtSt!o#D8}e=qL*j>bgo&z>v(TN&Iosu#*r21LF-` z&1}CZcI`6Rf^)3MqSzb%wxggokDxbqTs+qphJlR~RWOo|4E)WS*UuXtw|dWEPLJ1G z^q^=KKkiSs)=EsjMRnITU#&o2^g#vS9kMSrr%P~Nx{{A-xZ*mLcm07YR|hP^hG@;(jsfOpbqZfRz-RhUcs35OU*vlYrxK(oLvTJ z=G}cWGxmv&&o=)R06*bGHKAtY7^i27ROWcaKYMK*@Z{L=uyFpcFnU43DcQW{uju_z zteOK;eg8h3rT}!wpgpoUnSe?|HujbjwFMBRzg`{#>lNt{+W3VMlr@#dQ;Tr9Xze$r zy!MzG32j{;J1Yn#+*v^fLJdC$s=w!Wn_mzsfa_~vF zNseO-VRPzLV_(Hm-GKJ0>35>%!E(+x++Pr!wSX8e5p>v+yjlPkH0GJ+fhE%oo;K;?Oh22RB&Llf`sp-<%DjAOpl6siA3 zFhY%xJvA0L%Rh&9OTii2B&zTKs?+|v4uNngvG0xU{c|WEa(dMJtyA}(D-a7qQCY5` z98mB4#%MViJZig}9=K!*)WYS~`B5TMaN(zcN=Dl#R2Nb=LH?+f} zIQjXtAXXzyH`qr3=$`oZK>uC!f&Sf37-e?Bi@nL@F3lw>*#s_U*EkZdQf{L`VxCai zL&?#f`>vAmXM?44Ils63sd$lC^v+=kwhn>pD|~l%2J7?>x3>(ggwPp&fIlK%8>vcQ zrD6tZ+~eZ$Cm=rOgkdOhxFz`glQ@!^O0;_g2PX=Gg49lbep86RanIAM&l8Ma?n_dq zl=sKR!NJ+t+lzpmRqF0tpDFjV6KlY!GgY%A1y8;YqV|)6nq~Vf^$lH_NLG%nlFvT? z4DYr@@f)6mtNpz!FEv7SIlEtqIvI1@fVto7DdfKRVX@Cy-%O_@FEgMT#sC7xZiFw{(kL zYEjaW85c1JkQJxshoAjfGhucDMm=`t6*HCTR5!s@fL%)^#mZRl`6e>Cp!kNkFV-8- zOMf1|^z2M&1`1|E99K5T6qMK#YT~$g$snHL>Lue13<>Qv$VVHA`ec93u>Ze@hpHIQ zs`8yrv`)ctJk~Sa8ANs7Flx|s^GahS45cU`=NGCxEv*1Aq`@gl>xf-#GB^ZB0;Yby zlCeuD6Oh*t1JNA#FWVsl#HbRg52X7T;Iipx7qS zMB;&lsSQ?9Y;c9tS909JDuS-oX%FAO87O%jz+$+e=eidm4NPAmM{sUih%ru7TGXj= zP#S*iEy~~>-W~(F7$WhaH&d|8+c6u5Kq5hx3TE_7FlkgqC8H+w!uI7yN`_i>sj=^p z-1d-AQ3;>8C{RLR$Oh~@O31}iW$?uhB;^oehB-zH3>E2Y_g1e>RokvmqcbH5Ix6qL zR4p8T?$z*Cj(o{R%PWF_Zvhc3Dz#6~w{|q)f8m3mN=FFLAYup%TOY~{gU$LZPgvs^ z7>FjBAknCS?24>DV2i4w+QMWeOE|FlBOfE+F8sk;gcT`U`laB;%ICbFgP*qVqJr{= zjv5owP;>YV+kB2(+wMoQdBc~4vI767#a``SC(GuFlL|HjYClf-1?{*~mdgEDds^cv zb$AnGSK3gInYZH9AY?uo`4pSOrBKquey$(KVc@G7h)^_xDQp?p%$+zys*02N({Zf# znVmNUt&Oyr*Pd{pdG+9i7*UElj0bYq^E98&7C=iU$4Y}$ON8P6yj$h5cV;J4Il!VQmmJ7XH7W)q~?uY5U*&%eLZV7!_ zA2{x-6!O$GSKa=IvCM@?WW+ofU2g9-GaEY)Js|sh$+@@P*nzy~+crdxWtoeMWKm;V zmUrN-P`Yf3#vycvIqVO;_h+4R^uRi^gzo#mN+8AMNti!RrBRq&`n8mK#1TZ->%Z?Y zq?C4`zccBiCA^~Y(8Xqo>>ID)^f&9NXLU8l#x#{~YXOpcg-1u%Cbu_D z#49SHnmtO9Hr_%hmjC3*PztF$%qK`M(aBFF=KJP@$9vVAG@Y4{`ZHth+Yz)?QmJ6s z!1WBBx$t^i?9frdu*n^YdYFi1b^16o&0p>-ig<7F0TDfv?s|QDJdq3k=!~Tb`LQTBup|Hcrh@4HPDy!)p2lw#16fm$zM8ZmT!H zHcfi@TNOz)h+m=Ild7y97v9Nore5!ZbqGjn8|#&4I|(IhcVuVG4xxyg~0RooE+|RD7B!!S~Kfp z@!#eQi|1QMyMg-b~{jUJHVryYcwqdqZvq zPCKFKcyEq(q|1)%d)jLyBcA3xjEKbB4BLW`SS=u}@g-hAK{-55@%qj57ho}61mp6L z2)8)r|!%06GZ*EGIt*hwFVYyubQgdeb-sz|Ek3HKxFTMl5O|WYoPdkze6&o zWuDls?fi4);;-lF`@;RDBq)U+-t_UPZNq;gz46G$7-NRkr^_yy^wB1c@DGtJmCR%# z2XVqi8w~HUyBXxgt$0R$lF+dPlgFrMj+7s=pBR39O;A? zPgDHPUGf~F%Z)gmem2PrclcJlH@O5Gal_E{QsiYtn$}&XZZ;^g2^PlsO&or{F550P zKKpV{F=!_`9lbPtp67QRrG`ow9X<$>_o#iGc_#Newo>$VSx)Lg8a!XO7p)A49d?Dn z?1>@+1ZMlaqdMGlsInS3{4d?GD8Nzp+9)is@?3{vw`uSxkc1*wf6vn(erfB8nBhBY+)gzfMlSAE`yn1+q~P~#TF(H z8kJ6*u1^4WEO{_>Pr%~V_aKbb)@?r@*$}0Vd_GZR#WTN!n4zSe7clEd+Rk4i=!)r{ z-&Xmu*j`{3DnQ;e+1uUEfnJ#!ol?Wp^FjO*#;YNpyZXwj66F3lEuGItd|P(uO0oA> z@V~5lji(*Ci9ATdwTz9mO@2V37}bUO=o@c_s}9jBy-KKYFzVgyCmrf zeM@FvINd*oMk3{YY4W@R|A^2t72s6%Vv6qmJ+xgJ&bWAJ#mE>5p8fAH)gF@JCj1~$ z@^6y{pZ0=bB>(g}>wi}c5SvtCIF%vePW*okl|gbol|RHK{3AjiVwjCMy2sD{e0P;= zceQBn3PB0m2D{!tE~6(3-&M+G`M;-wbXA(=q3j$MeZSj@(?-YNx|EnY#{BO$_@PaK zfW{8(G}c1Dl9hl$?U%nNcoS2vycXc6dh& z*9X{0?}4W`8xgZgC=i6=+QN$Q2nbSEFJHbaEzgtw_oAVcgHKABb=?tz#>lm51kq*3 zt}L2S!?E+8oR1z_Wi^oAajOB=)Lr=`;TzyUu5S2CI1j4xCsFmreUvc}PA3Rf>dcnN zHpz9w%V~d|3Fc1^s*Sp%9|?u$Y<&ItluX#A0>oLInupQRzwf;V+EkE8tOH~HjJ1+W zv%F->tK40k%;LApBbh359--gCUJR)xPLCeC&>V(d?X;IAc=R>p%iJ6S*B^)wD#v~C zd^AXpEF=PR&HFaeJr~($78!QYgFPBiYCmENgMhxBCPXl~CJ8wuL8|hRSWswrk5L$O zKk^YTt9-G@sJCsY2}7I;`$k9AVE{x2v_Wf_MX*?!Le;$hG6pr1IrHHSQ73dmbI|8~ zduX_3@XPG!e;vN(ciK>T%FatC3SDXbW}L^e2uE6~B*Nl{Z$~@Px>^W4nN|hRu8R!- ziOW{wiVwwI13+XKftaQ~PdWKN;TW$`m;}q#EDWwLBzC}!L)yPT0ewl)(|&5CDiQv) z!zNG1%q$H;AR6e(Q1Jv{-WhQUM4EI47@uTv8g-M>X&|VOp3)PxV4X#Givqj@R@eGd z{MIMS6^wf0nLtgh-UiedTF_XbTCPFK7asZ&HxJSXq2!QrynE@&)r=Ms5R)-ec!m8l z?cWq>SoGqgU5F*z`4&FN*V#G0SNo)^<7Vc_ySF-(RZ`3gf#Edeo37JRK9xw$o}hiO z{$m&Ff$!_X4={KfC=l%hI9pJutIe|&VtbF`Qg*^gWN;!FONp42?{-Gs$o8RK_0bsi zl@b7@|AWU!Fw4{)A_9k;q5O}adMQCbDJi>-frCThK#%a_}!HKBzi zklEi}<+GM7eEjhVm<%jEnl|#kOFHpN9Zp|W8)3cNo4_(NGb7#Z2IEIlYTOpuqkI3* z!t9aM+Zc9B;a35HZ(Wz07Q^I77DcS4@C7T2$mTJd=Ue$^Sg?H35V*NwJz8o2kE-6j zDu(x1u^w0Y#S?V% I;?!S>cAu7!3%*AR8KH;IsN%TtfC9|r;?Pr?6)&!XgPM^* z{|uDUEGR#xZ2CH2M)(9I<^zCOEH0{${qXzyA`ZkLt^b-jkFi3ug8jRcx$DbUH-{qm z7VD_{2bNDS?;GR=Sw{3>a2kbZ(t6ibGpQ3jUBWp(}>B_H;V{1*ohH2V#z$c&~0kIOe00P>H=#J$9AR$JZ>~Qp&ie zgkWla5G-4oAF(jwB9~wT;q;oYcuWgQ_e`rO=FDo0sv&r_zRS9+OHO}AM zz6C)RCzS{Mqa*F<^5BD<%<8gp`QsNP?9otq``GQ}mLSoB7kbp1&NH}0x`!zu>;(>O zx1y9ppE%vKi%5_izwfk+MmhL}Ja_Dhpx#!?(6-&I@YdC94^A4~E<6)&-s!YumWkmk z*qiu5;VFXB)3G-blXc=`AmWq;1cS{2&e`?pqzDtek?7$Sf6i|x^n7OfKElO5Iy{@Xpwd;+BlGbP;GWW9!(ofM;Uk~d!UX8Deduhv{o z(pUnvOGkexIHkw)&F$S7{H!aYT4D9*bwEH7@VF~%02a1HM&Z;a03tjNo+V#>{Yy8n z9gkUl@FcpQIwG||4?k(wI@t*T&OH0*?8WTi#3uOJP>iVSeUE{Zm3oWLAb5S@TlRFI zpqv2F>9I}(B)^OU+H1yM`~9szy2ut>f`cRg)SrQz`^CXnx*SCu2zWlWwEXD1#`(k_ zQ1ae{Y+=ZO>2lBW`UHafqYy~vG(TOtIP;pb82IGR8_P!9z_hl}_7qUo^+;`t=MOLm z+gF8E+=0A$`T}GD;%D6Bzh8|S1nz(bC*$18w9!u6z$uy#(?-H&N~9F*80=MZ;q;J6 zOwT4tG6m*f`+G6lfO6Im%L2%~^nm~Z(Fnuf`Rhyj%+s(Ok5t(`gKHbjM0Er3D|^Oq zd$2yXK9Bn|r6gV<1SDI}BhkxFy9So0R%Anj{+t0?H&gSe# zx;~iC)Qp$Lt)`EA{w1TuPa7j~U2y!hU>1QQTjyO?We9||Rb~srj%U9K z6PTZx^4C69>U!v;IAN=+$0)?Wz-RA8Gd6F!*>J4t1EV)1M^gxxyN|oS|U!n$y1OApG8OMCq_%8S3Z2051tk z;S6F5lOO!sb>XGoy~H-u6B;5$fr?)Q*S5-8O_Axi0pW5X#d9NV`1-?UOwRMUs(NIa zhDK*=^`~8wjth7c5aA>j!%3%kXTUV-o*YQYKD&DOJ*}0dn$rqLsv+BHtVaJ#N zb;jJp!vcLz%mwc2DlxqfIz5TxjgqJ&3cF6+_|(r``^a_csIl(+aS9X4YVnf0sqP20 zp=Y`os9%v$YjED)`*$>|*Tw(0UwTf#(t>43_2z)Gv%z(|Ft_`Iny~XCn5Nji*7>e2 zzNzH^hB3^S+0N_f=FL6Hy{xVzZ~>Fpc2dhFSziv*_{G+YImEKr9zQrGaB!O~TV;BX zLB(#^e@s~#pwm36+uuY?1Ph3F!iPEY9#0hy2Z&M!2IKI0pZjUCwg#bwp7xnUO!=LhmHTf90Hk|B+%9b8yx%L=$o9*-t zD%$8%WsVZcCGZbp^FDLX8Xs>sjMP2ulxLoI;5bNQ%_<7hr5Nu|AH7potROOY6_w+UAMrk=b zSN2(jAjvd+xz~T|93EpR17|qi*Tjv712TPk@Bh?wbHr5QWERi!{uGJ}oKT_Z?2A-h z`%|G!K?O8NyzKO+0AeG6vdIu9eDcqp+jK)j68iJ&m_ph%mA?}b3J}h4gQJdKp;{B!^C%c<5)2!3pvuYo;GlKR06q2a|ENB2gi5FU-x()GvZc(&S<|SOgY84)G7|ppcMqA0M=hijlk^XRxthNX;2Oh`u%=L z(5XU&P#-0UJDQhvt$!Fxj|~4#!J=C6uGDcY?sZ+5K3OXuE!C`PzIA}V7z65IHL6)* z^4X5Q}`=Z|^82SRI zXXu4+a<6^VN_G<07`Dk7f}Q=pxNH;~b98ZRcNe4%9Kk_q%F0%2 znfa5-eP!_l&wJvNou5;8ABfL0ozLW-k!aq8Ee6Xh!2HjG zHhS5|EaCuiLpg(Za^%d_4|?K2Pr$dL3LJ6km=<8USHHZ83?b-Jaawi|O+L{6taX@+ z*n($8118?bfikC%!j`P~JyNC~c8+YpYa6}^$#e^s`}Riz+i zJh?I=mL=<+7=M&kLmKg?pI;iHeynX=Tp>CUJx6r1$RwxUuZ{EHbM%DvrfaJ+aS>uG zkc~2UUP~oxIN2;sSo21wX;FYcw&{?>?>d1h)T!n+?-bF&Z)gCW3YQ0VngS&o#Gm_O zj|32UmpdOKet*{^*lDmRxJkr*_vh9hpsU|>$(thj-PQkp^v8H+Sm~^!U^{Qzb@3|A zQJOG~U?L8dvBv&=vCetk#PAUmKU`mqGvC`xiCj3YXIM5bb7lC_Z2L^-4}2j)brluR zqbmuu{j5pjJ3or|M{bNBZ5g)bV876meRQ^^PhsGsLuST%{QJoSL`ae93Cf}ez3SA) z>1foQu@0_RYOGZr3EQsaxtgsO8M^e7j8F+0LwjD{(sGwYIn{qSU+;4O345aXhOxRh zOc7w=tr`aptDL9($>YPU8-MDJC>`X_9*!`W>Jj=>k~Ph5m-bL;hGc;*ruI{1A|_k$ zS}&541m=JR&+=isE(kBDyhhI9eu7Jl~}|v)1)g_p4U|U=G@V{OUyRz9i8tSWa~AO_xtF z1@yut;Az36_3@Rfg8Im$F(HswZQ8^1V;v}2^2NfJeqSEXcxI~(>O8|qoXPc`V>6L? z=8;m13+4LDVz@-N=xJ#ypc7}*hc2fG+3_kXzpXu5&EAJSN*fT!TB@_81l>J;j zoLl~G@$-GUYBvSByX7?Hw_eR7k=NhlWnfo&3@J0y!aXSMJ0*&Hu!(}h%3 zfR7SY!43kssPPgCJMKPN1Hq}uQyrAAfLpJ^j^zygU*%w8S><5~QvMN8!Gjtw7x>_P z7lI&qBfIu+DQ+KjJq>7bkQGp5X!LAe_CEKAVwl~ei)`E1A17e4t-%twv$Kix^|;O4z!TxgK^M;N*MMpOO^jI4h>(z3oX=$O$}SD>oY-xZu_) z$=vWjX&RdE3Xc+)C-WewR4|l;KD-_7n~Cf3o}KtgCl8o?4r~nCe+(d+_M$M)zGXf! zWXPbZ@Y)CLp_lg8uA6@tj^xa~97u#anyWqPe&u(&>LHSP*@yLA_g7zS750r8DL%)K zY_FMA8g$o{*;5{PQK~QHgBkY=_`uaU^MDyzzQjB`(xSfL#e8`vJQHZv;zchA*O?p! z6-%Yf%*=}BJK3@r2`ZtI;tjv1(I~)fl~hJVc+su;`q?2vZCN;;YO9L)D!A(;A!F+c zpp>|j*vs?hflxw4B#Bq*uetGt=gzIaPqMJh$K6G9;>~zs;G}EPXP;%?h-v-3^Bvfg zRjKbzIm`!jcu8&XAF0iL2t+18pSQ1`Grr({`Su=A+^mZ}3L+b27+WHx>s_R#!71wM zpGS3UT?@P<=?naVOhHuxdFb5wO33anZ;t$xRd9{AFTIo007mUcFw`$ zRkdyFNh~iWx&H2Wxm{Ef9UMB$>}P zc<#5|IqgqMiw6#sE2NgNHD@@1@5ujY?@I%r?Ax~^QzE4WMWVXP7Rs6wN=gy3?<6C; zB4im8mF`lg?6N1z*q0gma(9zPoF-W`2P9c4P-jQQGLUJ%@VZxiVEiFl)9|!c~^8Xt^(lJVi_ODXi_@VyOS# z@kgxaAUISP#h3pyD!g_=;5aAR#9rF+lyt|+?ywWZmb;jy#O2wGv|~pbvRnDU>FY9^ zsmZF&=q}cpu4>w(hIc(X*IRMXyC)UrUg`5sUiU@JdS2bFc{XR-(_M|JPeex0wHxa? zDU0nsi?`r{_TIgHs>#r7vyG48KljtoJ<2h^qBkSd>ynpI$2&F@ODhbqEu;y&vyj`l zlpeJ+=g&nRd2F+L|M5qahWgmDJKk~qf^eaX)xxw5)(^eg;-uubljxsaiaq>{bztOK z{BvoT@>nAVd*tXC9vmi~pk2#L{ zgKMBx%7I=$dz{wj57|mX=a>TsMa9c~k#qY40-!_xnEnyX)>r;Nzacrz3(}OC)F=rM zxq93#^{0mLnwL&$WetdD9BdSNAh>l6Kt*AtH-HCq-lS(=7p|0D_sh_8;z;;4(lBue zK^VmxIt`;e@lx9_c(CUgy4s*}_^gSAqv1E4;klGZ0al}m&33Opw*`D=a*ZtTajmsf zzK8w0&yARmDkjFNlRBoQSPP_;dcf_R1!0C36Cd*U9l*l(y9rW_R>m=qwmu0OI(7PI zr(=~LHrQ@TLAbpJcbL&>8JYfr2M^}pq`IjezGJDJ|9)PjV1D$SJ9i@)BBm%$2X}3K z6Yn$zxUE{vK77N^5c;ZyZEI7FQe?U_hcDu1uX$>dVk|c0U+nu$)I*2t6a&0!7tDtZ zXO`QMB&Hzgma>ZQt5tb#gM!SFsQ#sima3q9!(59uNQrCfko-JUQ?R<#*0||%kI*Et z_G$7Tx4S$Q>?%-{pDH~^$up^v?K)*>|q8y z8UX-F@x_a*rM74ard?`L8%Qca|CKgBXeH@dO07R!6?zQeoKDqfs|8Wqj&S2 zSKndRThN3GfTU@1oySo{CNvh5o_0vVVN+fgupE}Xc>{eqNjcmMl5JgHfThG58F}M4 zsk7rE%rclK{n|-=KQlO5VKcO}V_5v2mt#8iHpxYR`S4)op`52&Z(b8b zpNDs54&Y>&YZ9FQkx!*SW1oUKc!+GN!M>*AWo}`{XP(v4tp%rV1)XEkvdJeHuOvLl z21w%hGQPLgg6QzfOAGUfbIaqo^g>)JEcaKefNnxFf zby=deJI+#Z0|<#_m7m*MYzwWFOzcL_0NG;w<5~Sn2^Gx-YQt#mQg`ScL(U5cHKKU{ zmJaTNUGsf7GgXfiNXNDj+MrH;HygqY`_%nuX?=+|L|>Vg)a4-nB3 z76?|~_0D^Y|6IN8gXAX~pQotD^$zb5+7tVELuJ07Tw_^#zD;N0=p29Y&l)a0E7+EZ z{{h|h)CNt{<*gHhyBYb)=AI^5>R0|J{h#=&ZIz$`cBix?J(a+Y_S*Y3V)ee?17Ml^ ziiMx<@>i9u%d*3}`VcTSkkn)Cmw$3>_QFI*<_1bd@9oeOvGv#2kVit@!tWjnHKAe? zhxYeecBM3y$53y6crh!yIOT|=o;pg}f=}qJrNp%FO#5t$U=7;y>FgJVDuby4`I?ml z#;qq%jyoQue$C@RUq@^d-2G9UvR=KM5bT}@K->WQI)Wx;o>!*Ef};?g#Hj%MDGRlN zPBDK@gay~v_3KqFm(FwSGe4sw4K!pAWC2SUzB%)! zu7h#mnehDl9?DW=6q#GS@}t-N8#j+$uzKzWXP+Hdbu!kFUM}JK8(Yv5@nA0F_#@LY z`AzlyZq?%I`=P7YtgF^ALLr~$j(TkOUFoc<>Tw5SIb~c>qGP$SLG@Sd8IsZ zJtpVaW%pM1m6_>GNzD2qou43o5@6}(WEVM4b;D?sQC|fKY@K?k!{AHI!)2CrUk^BL$M6U;0IgsTNdO~V8v&x` zt3dJjN&6Lcd=yF`N-f020hpI$6SG#71$7!BZ5dX7Q*8wg6Q(*S8{a5&E|qp%7amj? z-B>AxXe&f+N#QE8hfmKAtXwl?i?wIm##h0}XA7ma5qQO#ly39gTYb^so@e5*X+}AL zZLj#uDf6hH_4ZjmLD-hyDBqY_GXDxG{3v&8*yQOgMDGE^EyVl|<6MMGK=(aLgZtOM z3_a0Yc4GC9R$H(E_lD5w2QUcKar2*p#^a+(wxEG0t{I~t3h>7g;h%9CMYTZqp(T4C zJZ;M`>Os9ct#l^lls+s2(yS5}%bXGj)If3^8LvCrp#V{c<-c(+iX4)-qq&3T__TXc~ zs>5phbw@k7P`=(@Y?Q`enxkRBzDE7Dk#<|Lzcyj_t^foaG(4^%3tfM9{AyRZ|KBAn*RX z!|_<3iw&1sagLe}NDDj@=Nl`2{}-IAco=zXM+Mh*TK#zQinWPOfL)>=oSKpQCzyD4 zw=21A6isC~If(^}w=OwnjC#2%kZ5;ab--phpvoh-QaxII5_WTWerNI%z@-kMFDAst z8xAH{ctAdY{eia~#&9A=#juIlynRdC53y=BXrcVUYNizro;hk4|B;zq&o@UUs+&&; zZQr(ZY+x=|XOUbDJk?`a;c)}`9{oxp3lo=s94OGF-ld@6R@^g$ra^$4PD5#NGbt`s zy-F+3^ai6l$TnkMHkEIDRME4Vth@C_12;HLR~U&2$9^%-3ka z=wYJufk4TUG@Ha@_ByQMS%}H{Q^2bkjRj$?!ekPX^!AaIp=T zb(YbuA+n&%+B|83XvI)Iwf)bFEGruT&l~O1z}%@|xnN5)GU>XBUOlXa)(q0pbP-7} zs`R(vH4ImX8b%)$Em9eLh{Dtg@t?xQbBm=u1!KHE|25WP7!bka1;FSal;4P7%EcdYWji-fJ7Vh8O} zdN3jG`1kHM1X={J*s}72ck85nBDV`V`=dJ^9@&O17-+T^F&AEK15s~i$al#dqk8<9 z6-}UN$8h#mo_wD>&t`2i>!C*p(SBCtNXoNSbzfvTf}74 z2@K_Qqx5WjZAz;Gm42ari%@!?s`Xr6>AeRO+|BkS1W4D$LBx(x54Kfp(KJX692<&7HuueSdh=UYL%KPVuwe+8lW z-p*ZnvkD3>=uE$n?9YN}>mG0oO@q>jhZ*!9cq$R-IGrWL+BLYBiT!LJRiAltb4SWAixnj_IFj_k?TLs7E zphM(;;q3+Fs9$~p9JU?&;sgK%znx&PK?qr3QdExyr8*rt*=0!g?E)n+9rhoRV{;&R zNa(^buxR$VPo?8w*sgf!l|YF_AE8^@11w?$4r44NI;Me)fWXj}H3Wx+8oSpZP3iti zb59seApqAJa2mArA>lqv91bN-Om#NKV=#fOTsaE!5owTKECKdK58y`a0DOVd@{Y!n z|GN5$NdM;u3_v@aE3R$OdmF&+q%^QZO27nX@x-UdUN5K&yqZmFZeV(_gXQPi;G$%^ z21DE8T5>6cL0AfDz{qqRq~PL(yul=JD|~u=AI1_4J%Gz_8Vljm_{u6|XbF-=Hk7r_ zjKWcNHZhrndQm*I#T`6Ru@%}^5^Lep>~#eXVz1fBm8VqQl!orz9_Zsq{?aq&P}p@| zIgN_sI3irgHE9Ha`n_5g$F~JKJ$jTUosottK-_dLWnzlrLb{ha5RUA4{gR)kE8yz2 z^z$r#n9z-&kFZo*7Z`ZsY7xnrK)$0k84QaT|AFblp>DNJK<$rTtZpA2s{wM=J8v;L z2@Gm*rv&KqV<{Mi7Qkrlh)Y-X_spQ4c`VQDVXsaRM8$#f>-2fTTG{&tls&^@2K^R( z0D2E;GPM1soFKw)9k**&XzgowjJl9TOl+S0U^XDT#gGqmQhS{Ff{IM5N@h7;ffP4? z_UqbeDTC>7PvN1v2#sq!FWA|1>3V}5OwSijJ!fsVB%6V!urL0|bSsHxrxGmmCIYZ8 zh3xBU^l4`@}kOtf)?djj! zXCd3B9;xSSf&2m~rs@h#oWXZCK68-=wS8>{cw#)wta}6)*h|&puA9%Q6pT0f*29CYGCA!*pSabRV+W;{k8rtX2<%Ts$G5i&Lxk33( z;H~X9$Ej_Qw($on=zzg?vYP$z}t*9hgCg1x&8sh3o4@Fd+y5qY-* zE1y%gPqn+OAbE>k2>oNR1420x>X*GoeDAiBX8|?t+*2ni56evezy2tmr*@NCVgH}L zjdx(_3y}ob1=dm0h$H>A4^LK-_MOVO_lW-Yr|x|g5vxWhx%ETuV=r#~Hi7b}BlC@M z`~B^yHvhLL!TrUj>f=TI=~9|dVPD8xD54_5Yp>w9_;_g#WM=;PP9tjYTO3;-9N!kC z_@J7Kk>qaBNL~amzZ#EXN&Oae6biSo$W5gA zDeLKUMkAzZbVj*Qprss6U28Cs=Kx3A4bj&(5uZBE&RvKDWg7CH@nKbx7wsr#M8gY_IyUl;ZmBH}l3JLN~KiS-lk?Dgmxsi3p9y>x;<rDP8^z$-*q&&uNfn`F3e`U;vT8 zEFwQ>%8A~exsM+)eJyyqf zQ0|nR^z}CDoda?buSGE#~m?JJ%svXC&HzAJ#8Pn+sE zAaEBI&#(9L<7fQkY-Nk}h}1^lyVHT&O7SVIc*U@ISS?XY<->)$u*mI!Ud_-KKLbcv zAtK%CmMH_O8{+85cnN=X0dqASVUzKYCYBc2llYg7OsNE9cMwIAATuuy>T&_uW?%B? z^~r<^oer%vQXQ5s+94^K&!(Fv$0H%pj&MmZJn8a6W_56$nZ!%!xE zc&0C=EEt_gtpjWV%pp(IwUj91NQRNaX_G8BCT9Wf_RbZlv!SQ;rQEQx|Z4zJts2lO_nUs4W zQ#J$6wS~`xBG{4#_}g$=ut@xC;axK(CYzZ=9Wf#fbygo^1R)fXp6PLx@HD|_PCmTY zy{*p`?$*8%dBNm860)MD68W?qKxRq+Yk|ZP8N_8sMKUak5jSDKLxG2yiKg-6A1KQA z{PhekKDiFVIxr45EUB&-!k#w|unrj+8JUFZIVc*0>)&SjPLc|y(W=0;{p|M}D0S&W zZ95C98Zp?zSsVZQgSMuf)t{KZ=qJ(>gJbm?uQ4EtD2t-aN*D;(7de!ZS{ zvBSdmb`4!sVn?znYPxiv=LgWGzb5YK zzQZ|>zRF6pw0{9fdEJKazfxF115MfOX??^y;67QLg{6qI9h0xwU^< z+%%pSs~8M^OQ_Enad?u%^zVWH{Pg)~xmK$I6BVgP+!Ae3H#jfbo=;iMIhDzHY+=*& zE%QiuXvxe!oBx3ZfuX1{`S8#fnHB#nKxh6G-jxJ6vZzoo6wuqke9Krr^^({!D|*~f zVSf`-vbIktOgJOZvmX}o#s#*14zoAl#sjNr;|I6C68Ii2nNxF1eCF2c%LI#yA;Dxi*&6_?KViXdp{%o+iHmkZ%+1s z$Oj)`1O061UyX3uMPt#wP{mOfUGuTz1&Us2QD~BZWct%#lSosrmE&jn=p$N0%w)IF zwz9WL0?u}Z0_JEtO6AjxHpj$-bfp*ktZboSll-TZSaVcIZJkI2+s#T0p`c7J)!k}( zwJPlV^6K}vqq~A)8X_HH8c6l+X={z*ZCO9WKgsyBuU++}NmRMuyq!iv|9QnyXYe*&!Snb4>Gp9_W-!^#;a>B2NdG7F>A*!5{-YWqdhJa; zG4mdshXNvHq+Mp_(HzB{Rid&v02LC`LMB~dvf^i5%z&hArbpqN<3Q<(;cCqX>iopD zEX4TZ(6yex*q`G#tG_m#PHwDq^L8%Z=+M!|dn&A?xGL;4Ab4R0@8*8|YlURkTsT6~ z*jP7jKihHr`K{0{K%sVh@$23$aHGnMc-*1f0@8Bu>F(7NxZi_W=8uc0-m~C6xRw08`-JRe^0mAF^CN4eG7~A$Lt}Y11y}KZ5VrtjpoYf|v zqbojesjfZ*08Q86ix-_^3u9}e_q9cta2^T^d=67BRqjhf^zc{mXBaD)C?4fpZfO4E zV$@=>3{C;PI#$86<-)kaW>@0k#03Ez{D)~Ub;_Qle3KlQ&zEW^Fa5lV(~XOl)#a3& z(IQPweZiB9#a&jb57_j5N*6SoD-0Gg>h_gcE-1cU^DYsL`2`KIm8IkK_V_i79_Q>$ z9ObcoEG6*MOeCmw{k9j$F7ndEZ5Ua24uWFsdaqaH(o|0e+eb{)gne?L6=O6A_4}?4huU%La*4H%yqYy=@xu9TbW%s zef7Pk-{bzgvFJ@d1qGG|W24_U7zsKJKd*geQELxu8Qh^9aP@~J$kFjzfDT@JL|Y+ zZ_Cnm&wmccaPPaAzf-lY)dk{E(kG-XFc;&$w^*;;h5@vbH|&M9rFaGhw55 zcV(=%dj*47Q#iFdI$@f$dv6wOl<4cnxW4LZC0Q8c5A~RjC8d4ap7S?E7*U}q4`FlV z`pnAK`C!uY_^;d)857~Y;c^`&Qums;C>aypr{+87FTLJ5c*D07PCiWL>didtns1S~ z2?$vG!tK5?sk+X#Hyroy44YnNx+lOLb?%2VWi8*j!?E(`?%8sOUuAL&$-+IW6CVo8 zevB_dgCvT^!=bMn$r-Q-=JW&t&eM_Is1h#38w^9l@3_k?wFoc4L}jR}vNAzU2L z#WdW+b5})v8;?3fB+U%;p(D!%%ymq9syWGZqF5LC22?`4(br#f&&$5ZOiOp4l0WWT z^@61B>LGl#1NZl?e%T5F_qY?zxYW0Njkatpq&A*^E^4m(@j&1@S2{VEsrOg$4Do(L zgv1gmV>sObU}%y(hEN{upzGo7a97Q9)FcI57O6idq$;C&UMrD8yg(t81gwqjjn3kPsd=rk;DISgui3It zuZ8q{+x~irK@zW$&bjJyj{4I{`6l7=rM@>lcZBcZZm}Nw_vQPtw(d1$Zz}+lAB|m% zc`tUyV`3Qjtn#^RDMtqe6arQnX3rX@>FTnMm{%>7GaqWKk@3!bd6O*3nA{dDpi#bC zV$S+@#D^F*&)$_HoUVy>iPz==<@1@WeSa9EkB5JQMLl12HjM}4=wk`U_-Q}Ee@b`I zVY<#=Jy|V|XnCfOEk3z>{ z>f3&_2bQVIplNz@d2*i0a&ZUd(p|;#^4vfF-GEx_y%`~9qC`!qkxJ=7tOs4t#tW+a z#Qoq7tzNXeo@s}_UlBLY*j}1wr^d%BiwFP1!znUXJId+Vj=okLm&8!HVdlS?X?#mg zja>Oo)g8E#kO`Vro*U&j}Gp+IQns1*C(fE51ERo&k2cs+`kyxC+jg;^EPf5kZ+hHHD(9=xv8LXE#s=G&;J3CdCg4# literal 0 HcmV?d00001