diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..21c125c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +.py text eol=lf +.rst text eol=lf +.txt text eol=lf +.yaml text eol=lf +.toml text eol=lf +.license text eol=lf +.md text eol=lf diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70ade69..ff19dde 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,42 +1,21 @@ -# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries # # SPDX-License-Identifier: Unlicense repos: - - repo: https://github.com/python/black - rev: 23.3.0 - hooks: - - id: black - - repo: https://github.com/fsfe/reuse-tool - rev: v1.1.2 - hooks: - - id: reuse - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/pycqa/pylint - rev: v2.17.4 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.4 hooks: - - id: pylint - name: pylint (library code) - types: [python] - args: - - --disable=consider-using-f-string - exclude: "^(docs/|examples/|tests/|setup.py$)" - - id: pylint - name: pylint (example code) - description: Run pylint rules on "examples/*.py" files - types: [python] - files: "^examples/" - args: - - --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code - - id: pylint - name: pylint (test code) - description: Run pylint rules on "tests/*.py" files - types: [python] - files: "^tests/" - args: - - --disable=missing-docstring,consider-using-f-string,duplicate-code + - id: ruff-format + - id: ruff + args: ["--fix"] + - repo: https://github.com/fsfe/reuse-tool + rev: v3.0.1 + hooks: + - id: reuse diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index f945e92..0000000 --- a/.pylintrc +++ /dev/null @@ -1,399 +0,0 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# -# SPDX-License-Identifier: Unlicense - -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Add files or directories to the ignore-list. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the ignore-list. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. -jobs=1 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins=pylint.extensions.no_self_use - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call -disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable= - - -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio).You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -# notes=FIXME,XXX,TODO -notes=FIXME,XXX - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=board - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,future.builtins - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -# expected-line-ending-format= -expected-line-ending-format=LF - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=yes - -# Minimum lines number of a similarity. -min-similarity-lines=12 - - -[BASIC] - -# Regular expression matching correct argument names -argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct attribute names -attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct class names -# class-rgx=[A-Z_][a-zA-Z0-9]+$ -class-rgx=[A-Z_][a-zA-Z0-9_]+$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Regular expression matching correct function names -function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Good variable names which should always be accepted, separated by a comma -# good-names=i,j,k,ex,Run,_ -good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct method names -method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty - -# Regular expression matching correct variable names -variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - - -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of attributes for a class (see R0902). -# max-attributes=7 -max-attributes=11 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of statements in function / method body -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=1 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=builtins.Exception diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 33c2a61..255dafd 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,8 +8,11 @@ # Required version: 2 +sphinx: + configuration: docs/conf.py + build: - os: ubuntu-20.04 + os: ubuntu-lts-latest tools: python: "3" diff --git a/README.rst b/README.rst index 68f9ed1..e6bb3cb 100644 --- a/README.rst +++ b/README.rst @@ -13,9 +13,9 @@ Adafruit_CircuitPython_AdafruitIO :target: https://github.com/adafruit/Adafruit_CircuitPython_AdafruitIO/actions/ :alt: Build Status -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - :alt: Code Style: Black +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Code Style: Ruff CircuitPython wrapper library for communicating with `Adafruit IO `_. @@ -26,6 +26,8 @@ Dependencies This driver depends on: * `Adafruit CircuitPython `_ +* `Adafruit CircuitPython ConnectionManager `_ +* `Adafruit CircuitPython MiniMQTT `_ Please ensure that all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading diff --git a/adafruit_io/adafruit_io.py b/adafruit_io/adafruit_io.py index dfafc14..0117adf 100755 --- a/adafruit_io/adafruit_io.py +++ b/adafruit_io/adafruit_io.py @@ -18,26 +18,28 @@ * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases """ -import time + import json import re +import time try: - from typing import List, Any, Callable, Optional + from typing import Any, Callable, List, Optional except ImportError: pass from adafruit_minimqtt.adafruit_minimqtt import MMQTTException + from adafruit_io.adafruit_io_errors import ( + AdafruitIO_MQTTError, AdafruitIO_RequestError, AdafruitIO_ThrottleError, - AdafruitIO_MQTTError, ) __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AdafruitIO.git" -CLIENT_HEADERS = {"User-Agent": "AIO-CircuitPython/{0}".format(__version__)} +CLIENT_HEADERS = {"User-Agent": f"AIO-CircuitPython/{__version__}"} def validate_feed_key(feed_key: str): @@ -54,6 +56,19 @@ def validate_feed_key(feed_key: str): ) +def validate_n_values(n_values: int): + """Validates a provided number of values to retrieve data from Adafruit IO. + + Although Adafruit IO will accept values < 1 and > 1000, this avoids two types of issues: + n_values < 1 (coding errors) and n_values > 1000 (pagination required for HTTP API) + + """ + if n_values < 1 or n_values > 1000: # validate 0 < n_values <= 1000 + raise ValueError( + "Number of values must be greater than zero and less than or equal to 1000" + ) + + class IO_MQTT: """ Client for interacting with Adafruit IO MQTT API. @@ -62,35 +77,32 @@ class IO_MQTT: :param MiniMQTT mqtt_client: MiniMQTT Client object. """ - # pylint: disable=protected-access def __init__(self, mqtt_client): # Check for MiniMQTT client mqtt_client_type = str(type(mqtt_client)) if "MQTT" in mqtt_client_type: self._client = mqtt_client else: - raise TypeError( - "This class requires a MiniMQTT client object, please create one." - ) + raise TypeError("This class requires a MiniMQTT client object, please create one.") # Adafruit IO MQTT API MUST require a username try: self._user = self._client._username except Exception as err: - raise TypeError( - "Adafruit IO requires a username, please set one in MiniMQTT" - ) from err + raise TypeError("Adafruit IO requires a username, please set one in MiniMQTT") from err # User-defined MQTT callback methods must be init'd to None self.on_connect = None self.on_disconnect = None self.on_message = None self.on_subscribe = None self.on_unsubscribe = None + self.on_publish = None # MQTT event callbacks self._client.on_connect = self._on_connect_mqtt self._client.on_disconnect = self._on_disconnect_mqtt self._client.on_message = self._on_message_mqtt self._client.on_subscribe = self._on_subscribe_mqtt self._client.on_unsubscribe = self._on_unsubscribe_mqtt + self._client.on_publish = self._on_publish_mqtt self._connected = False def __enter__(self): @@ -128,7 +140,6 @@ def is_connected(self): except MMQTTException: return False - # pylint: disable=not-callable, unused-argument def _on_connect_mqtt(self, client, userdata, flags, return_code): """Runs when the client calls on_connect.""" if return_code == 0: @@ -139,7 +150,6 @@ def _on_connect_mqtt(self, client, userdata, flags, return_code): if self.on_connect is not None: self.on_connect(self) - # pylint: disable=not-callable, unused-argument def _on_disconnect_mqtt(self, client, userdata, return_code): """Runs when the client calls on_disconnect.""" self._connected = False @@ -147,7 +157,6 @@ def _on_disconnect_mqtt(self, client, userdata, return_code): if self.on_disconnect is not None: self.on_disconnect(self) - # pylint: disable=not-callable def _on_message_mqtt(self, client, topic: str, payload: str): """Runs when the client calls on_message. Parses and returns incoming data from Adafruit IO feeds. @@ -183,18 +192,19 @@ def _on_message_mqtt(self, client, topic: str, payload: str): topic_name = topic_name[2] message = payload else: - raise ValueError( - "You must define an on_message method before calling this callback." - ) + raise ValueError("You must define an on_message method before calling this callback.") self.on_message(self, topic_name, message) - # pylint: disable=not-callable + def _on_publish_mqtt(self, client, user_data, topic, pid): + """Runs when the client calls on_publish.""" + if self.on_publish is not None: + self.on_publish(self, user_data, topic, pid) + def _on_subscribe_mqtt(self, client, user_data, topic, qos): """Runs when the client calls on_subscribe.""" if self.on_subscribe is not None: self.on_subscribe(self, user_data, topic, qos) - # pylint: disable=not-callable def _on_unsubscribe_mqtt(self, client, user_data, topic, pid): """Runs when the client calls on_unsubscribe.""" if self.on_unsubscribe is not None: @@ -212,9 +222,7 @@ def add_feed_callback(self, feed_key: str, callback_method: Callable): :param str callback_method: Name of callback method. """ validate_feed_key(feed_key) - self._client.add_topic_callback( - "{0}/f/{1}".format(self._user, feed_key), callback_method - ) + self._client.add_topic_callback(f"{self._user}/f/{feed_key}", callback_method) def remove_feed_callback(self, feed_key: str): """Removes a previously registered callback method @@ -226,7 +234,7 @@ def remove_feed_callback(self, feed_key: str): :param str feed_key: Adafruit IO feed key. """ validate_feed_key(feed_key) - self._client.remove_topic_callback("{0}/f/{1}".format(self._user, feed_key)) + self._client.remove_topic_callback(f"{self._user}/f/{feed_key}") def loop(self, timeout=1): """Manually process messages from Adafruit IO. @@ -265,13 +273,13 @@ def subscribe( """ if shared_user is not None and feed_key is not None: validate_feed_key(feed_key) - self._client.subscribe("{0}/f/{1}".format(shared_user, feed_key)) + self._client.subscribe(f"{shared_user}/f/{feed_key}") elif group_key is not None: validate_feed_key(group_key) - self._client.subscribe("{0}/g/{1}".format(self._user, group_key)) + self._client.subscribe(f"{self._user}/g/{group_key}") elif feed_key is not None: validate_feed_key(feed_key) - self._client.subscribe("{0}/f/{1}".format(self._user, feed_key)) + self._client.subscribe(f"{self._user}/f/{feed_key}") else: raise AdafruitIO_MQTTError("Must provide a feed_key or group_key.") @@ -292,9 +300,7 @@ def subscribe_to_randomizer(self, randomizer_id: int): :param int randomizer_id: Random word record you want data for. """ - self._client.subscribe( - "{0}/integration/words/{1}".format(self._user, randomizer_id) - ) + self._client.subscribe(f"{self._user}/integration/words/{randomizer_id}") def subscribe_to_weather(self, weather_record: int, forecast: str): """Subscribes to a weather forecast using the Adafruit IO PLUS weather @@ -303,11 +309,7 @@ def subscribe_to_weather(self, weather_record: int, forecast: str): :param int weather_record: Weather record you want data for. :param str forecast: Forecast data you'd like to recieve. """ - self._client.subscribe( - "{0}/integration/weather/{1}/{2}".format( - self._user, weather_record, forecast - ) - ) + self._client.subscribe(f"{self._user}/integration/weather/{weather_record}/{forecast}") def subscribe_to_time(self, time_type: str): """Adafruit IO provides some built-in MQTT topics for getting the current server time. @@ -349,20 +351,18 @@ def unsubscribe( """ if shared_user is not None and feed_key is not None: validate_feed_key(feed_key) - self._client.unsubscribe("{0}/f/{1}".format(shared_user, feed_key)) + self._client.unsubscribe(f"{shared_user}/f/{feed_key}") elif group_key is not None: validate_feed_key(group_key) - self._client.unsubscribe("{0}/g/{1}".format(self._user, group_key)) + self._client.unsubscribe(f"{self._user}/g/{group_key}") elif feed_key is not None: validate_feed_key(feed_key) - self._client.unsubscribe("{0}/f/{1}".format(self._user, feed_key)) + self._client.unsubscribe(f"{self._user}/f/{feed_key}") else: raise AdafruitIO_MQTTError("Must provide a feed_key or group_key.") # Publishing - def publish_multiple( - self, feeds_and_data: List, timeout: int = 3, is_group: bool = False - ): + def publish_multiple(self, feeds_and_data: List, timeout: int = 3, is_group: bool = False): """Publishes multiple data points to multiple feeds or groups with a variable timeout. @@ -389,7 +389,6 @@ def publish_multiple( self.publish(topic, data) time.sleep(timeout) - # pylint: disable=too-many-arguments def publish( self, feed_key: str, @@ -450,18 +449,16 @@ def publish( """ validate_feed_key(feed_key) if is_group: - self._client.publish("{0}/g/{1}".format(self._user, feed_key), data) + self._client.publish(f"{self._user}/g/{feed_key}", data) if shared_user is not None: - self._client.publish("{0}/f/{1}".format(shared_user, feed_key), data) + self._client.publish(f"{shared_user}/f/{feed_key}", data) if metadata is not None: if isinstance(data, int or float): data = str(data) csv_string = data + "," + metadata - self._client.publish( - "{0}/f/{1}/csv".format(self._user, feed_key), csv_string - ) + self._client.publish(f"{self._user}/f/{feed_key}/csv", csv_string) else: - self._client.publish("{0}/f/{1}".format(self._user, feed_key), data) + self._client.publish(f"{self._user}/f/{feed_key}", data) def get(self, feed_key: str): """Calling this method will make Adafruit IO publish the most recent @@ -477,7 +474,7 @@ def get(self, feed_key: str): io.get('temperature') """ validate_feed_key(feed_key) - self._client.publish("{0}/f/{1}/get".format(self._user, feed_key), "\0") + self._client.publish(f"{self._user}/f/{feed_key}/get", "\0") class IO_HTTP: @@ -537,7 +534,7 @@ def _compose_path(self, path: str): :param str path: Adafruit IO API URL path. """ - return "https://io.adafruit.com/api/v2/{0}/{1}".format(self.username, path) + return f"https://io.adafruit.com/api/v2/{self.username}/{path}" # HTTP Requests def _post(self, path: str, payload: Any): @@ -561,9 +558,7 @@ def _get(self, path: str): :param str path: Formatted Adafruit IO URL from _compose_path """ - with self._http.get( - path, headers=self._create_headers(self._aio_headers[1]) - ) as response: + with self._http.get(path, headers=self._create_headers(self._aio_headers[1])) as response: self._handle_error(response) json_data = response.json() return json_data @@ -599,14 +594,12 @@ def send_data( :param int precision: Optional amount of precision points to send with floating point data """ validate_feed_key(feed_key) - path = self._compose_path("feeds/{0}/data".format(feed_key)) + path = self._compose_path(f"feeds/{feed_key}/data") if precision: try: data = round(data, precision) except NotImplementedError as err: # received a non-float value - raise NotImplementedError( - "Precision requires a floating point value" - ) from err + raise NotImplementedError("Precision requires a floating point value") from err payload = self._create_data(data, metadata) self._post(path, payload) @@ -615,12 +608,39 @@ def send_batch_data(self, feed_key: str, data_list: list): Sends a batch array of data to a specified Adafruit IO feed :param str feed_key: Adafruit IO feed key - :param list Data: Data list to send + :param list Data: Data list to send (namedtuples or dicts with 'value' key) """ validate_feed_key(feed_key) - path = "feeds/{0}/data/batch".format(feed_key) - data_dict = type(data_list)((data._asdict() for data in data_list)) - self._post(path, {"data": data_dict}) + if not isinstance(data_list, list) or data_list == []: + raise ValueError("Data must be a list of dicts or namedtuples") + if not isinstance(data_list[0], dict): # assume namedtuple + data_list = type(data_list)(data._asdict() for data in data_list) + if not all("value" in data for data in data_list): + raise ValueError("Data list items must at least contain a 'value' key") + path = self._compose_path(f"feeds/{feed_key}/data/batch") + self._post(path, {"data": data_list}) + + def send_group_data( + self, group_key: str, feeds_and_data: list, metadata: Optional[dict] = None + ): + """ + Sends data to specified Adafruit IO feeds in a group + + :param str group_key: Adafruit IO feed key + :param list feeds_and_data: A list of dicts, with feed "key" and "value" entries + :param dict metadata: Optional metadata for the data e.g. created_at, lat, lon, ele + """ + validate_feed_key(group_key) + path = self._compose_path(f"groups/{group_key}/data") + if not isinstance(feeds_and_data, list): + raise ValueError('This method accepts a list of dicts with "key" and "value".') + if metadata is not None: + if not isinstance(metadata, dict): + raise ValueError("Metadata must be a dictionary.") + metadata.update({"feeds": feeds_and_data}) + self._post(path, metadata) + else: + self._post(path, {"feeds": feeds_and_data}) def receive_all_data(self, feed_key: str): """ @@ -630,7 +650,20 @@ def receive_all_data(self, feed_key: str): :param str feed_key: Adafruit IO feed key """ validate_feed_key(feed_key) - path = self._compose_path("feeds/{0}/data".format(feed_key)) + path = self._compose_path(f"feeds/{feed_key}/data") + return self._get(path) + + def receive_n_data(self, feed_key: str, n_values: int): + """ + Get most recent n data values from a specified feed. Data is + returned in reverse order. + + :param str feed_key: Adafruit IO feed key + :param int n_values: Number of data values + """ + validate_n_values(n_values) + validate_feed_key(feed_key) + path = self._compose_path(f"feeds/{feed_key}/data?limit={n_values}") return self._get(path) def receive_data(self, feed_key: str): @@ -640,7 +673,7 @@ def receive_data(self, feed_key: str): :param string feed_key: Adafruit IO feed key """ validate_feed_key(feed_key) - path = self._compose_path("feeds/{0}/data/last".format(feed_key)) + path = self._compose_path(f"feeds/{feed_key}/data/last") return self._get(path) def delete_data(self, feed_key: str, data_id: str): @@ -651,7 +684,7 @@ def delete_data(self, feed_key: str, data_id: str): :param string data_id: Data point to delete from the feed """ validate_feed_key(feed_key) - path = self._compose_path("feeds/{0}/data/{1}".format(feed_key, data_id)) + path = self._compose_path(f"feeds/{feed_key}/data/{data_id}") return self._delete(path) # Groups @@ -672,7 +705,7 @@ def delete_group(self, group_key: str): :param str group_key: Adafruit IO Group Key """ - path = self._compose_path("groups/{0}".format(group_key)) + path = self._compose_path(f"groups/{group_key}") return self._delete(path) def get_group(self, group_key: str): @@ -681,7 +714,7 @@ def get_group(self, group_key: str): :param str group_key: Adafruit IO Group Key """ - path = self._compose_path("groups/{0}".format(group_key)) + path = self._compose_path(f"groups/{group_key}") return self._get(path) def create_feed_in_group(self, group_key: str, feed_name: str): @@ -690,7 +723,7 @@ def create_feed_in_group(self, group_key: str, feed_name: str): :param str group_key: Group name. :param str feed_name: Name of new feed. """ - path = self._compose_path("groups/{0}/feeds".format(group_key)) + path = self._compose_path(f"groups/{group_key}/feeds") payload = {"feed": {"name": feed_name}} return self._post(path, payload) @@ -702,7 +735,7 @@ def add_feed_to_group(self, group_key: str, feed_key: str): :param str feed_key: Feed to add to the group """ validate_feed_key(feed_key) - path = self._compose_path("groups/{0}/add".format(group_key)) + path = self._compose_path(f"groups/{group_key}/add") payload = {"feed_key": feed_key} return self._post(path, payload) @@ -716,9 +749,9 @@ def get_feed(self, feed_key: str, detailed: bool = False): """ validate_feed_key(feed_key) if detailed: - path = self._compose_path("feeds/{0}/details".format(feed_key)) + path = self._compose_path(f"feeds/{feed_key}/details") else: - path = self._compose_path("feeds/{0}".format(feed_key)) + path = self._compose_path(f"feeds/{feed_key}") return self._get(path) def create_new_feed( @@ -757,9 +790,7 @@ def create_and_get_feed( try: return self.get_feed(feed_key, detailed=detailed) except AdafruitIO_RequestError: - self.create_new_feed( - feed_key, feed_desc=feed_desc, feed_license=feed_license - ) + self.create_new_feed(feed_key, feed_desc=feed_desc, feed_license=feed_license) return self.get_feed(feed_key, detailed=detailed) def delete_feed(self, feed_key: str): @@ -769,7 +800,7 @@ def delete_feed(self, feed_key: str): :param str feed_key: Valid feed key """ validate_feed_key(feed_key) - path = self._compose_path("feeds/{0}".format(feed_key)) + path = self._compose_path(f"feeds/{feed_key}") return self._delete(path) # Adafruit IO Connected Services @@ -780,7 +811,7 @@ def receive_weather(self, weather_id: int): :param int weather_id: ID for retrieving a specified weather record. """ - path = self._compose_path("integrations/weather/{0}".format(weather_id)) + path = self._compose_path(f"integrations/weather/{weather_id}") return self._get(path) def receive_random_data(self, generator_id: int): @@ -789,15 +820,86 @@ def receive_random_data(self, generator_id: int): :param int generator_id: Specified randomizer record """ - path = self._compose_path("integrations/words/{0}".format(generator_id)) + path = self._compose_path(f"integrations/words/{generator_id}") + return self._get(path) + + def get_user_info(self): + """ + Get detailed account information for the current user. + + See https://io.adafruit.com/api/docs/#get-user-info + """ + return self._get("https://io.adafruit.com/api/v2/user") + + def get_user_rate_info(self): + """ + Get rate limit and usage information for the current user. + + See https://io.adafruit.com/api/docs/#get-detailed-user-info + + Example output: + ``` + { + "data_rate_limit": 30, + "active_data_rate": 0, + "authentication_rate": 0, + "subscribe_authorization_rate": 0, + "publish_authorization_rate": 0, + "hourly_ban_rate": 0, + "mqtt_ban_error_message": null, + "active_sms_rate": 0 + } + ``` + """ + path = self._compose_path("throttle") return self._get(path) - def receive_time(self): + def get_remaining_throttle_limit(self): + """ + Get the remaining data points allowed before hitting the throttle limit. + This retrieves the user rate limit and deducts usage for the current user. + + See https://io.adafruit.com/api/docs/#get-detailed-user-info + """ + user_rates = self.get_user_rate_info() + if user_rates is None: + raise ValueError("Could not get user info, get_user_rate_info returned None.") + return user_rates["data_rate_limit"] - user_rates["active_data_rate"] + + def get_throttle_limit(self): + """ + Get user throttle limit a.k.a "data_rate_limit" for the current user. + + See https://io.adafruit.com/api/docs/#get-detailed-user-info + """ + user_rates = self.get_user_rate_info() + if user_rates is None: + raise ValueError("Could not get user info, get_user_rate_info returned None.") + return user_rates["data_rate_limit"] + + def get_current_usage(self): + """ + Get current rate usage a.k.a "active_data_rate" for the current user. + + See https://io.adafruit.com/api/docs/#get-detailed-user-info + """ + user_rates = self.get_user_rate_info() + if user_rates is None: + raise ValueError("Could not get user info, get_user_rate_info returned None.") + return user_rates["active_data_rate"] + + def receive_time(self, timezone: str = None): """ Returns a struct_time from the Adafruit IO Server based on the device's IP address. https://circuitpython.readthedocs.io/en/latest/shared-bindings/time/__init__.html#time.struct_time + The default time returned is based on the device's IP address being geolocated, + falling back to UTC if unable to be geolocated. The timezone can be manually set. + + :param str timezone: Timezone to return time in, see https://io.adafruit.com/services/time """ path = self._compose_path("integrations/time/struct.json") + if timezone is not None: + path += f"?tz={timezone}" time_struct = self._get(path) return time.struct_time( ( diff --git a/adafruit_io/adafruit_io_errors.py b/adafruit_io/adafruit_io_errors.py index c35c836..f3b256a 100755 --- a/adafruit_io/adafruit_io_errors.py +++ b/adafruit_io/adafruit_io_errors.py @@ -20,13 +20,11 @@ class AdafruitIO_RequestError(Exception): def __init__(self, response): response_content = response.json() error = response_content["error"] - super().__init__( - "Adafruit IO Error {0}: {1}".format(response.status_code, error) - ) + super().__init__(f"Adafruit IO Error {response.status_code}: {error}") class AdafruitIO_MQTTError(Exception): """Adafruit IO MQTT error class""" def __init__(self, response): - super().__init__("MQTT Error: {0}".format(response)) + super().__init__(f"MQTT Error: {response}") diff --git a/docs/api.rst b/docs/api.rst index 2e26b2f..c50c72c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -4,5 +4,8 @@ .. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py) .. use this format as the module name: "adafruit_foo.foo" +API Reference +############# + .. automodule:: adafruit_io.adafruit_io :members: diff --git a/docs/conf.py b/docs/conf.py index 82edab7..d211602 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,12 +1,10 @@ -# -*- coding: utf-8 -*- - # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # # SPDX-License-Identifier: MIT +import datetime import os import sys -import datetime sys.path.insert(0, os.path.abspath("..")) @@ -41,9 +39,7 @@ creation_year = "2019" current_year = str(datetime.datetime.now().year) year_duration = ( - current_year - if current_year == creation_year - else creation_year + " - " + current_year + current_year if current_year == creation_year else creation_year + " - " + current_year ) copyright = year_duration + " Brent Rubell" author = "Brent Rubell" @@ -97,7 +93,6 @@ import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/examples/adafruit_io_http/adafruit_io_analog_in.py b/examples/adafruit_io_http/adafruit_io_analog_in.py index 7aafb8b..dfd80b2 100644 --- a/examples/adafruit_io_http/adafruit_io_analog_in.py +++ b/examples/adafruit_io_http/adafruit_io_analog_in.py @@ -4,24 +4,24 @@ # Example of publishing the value of an ADC to Adafruit IO # adafruit_circuitpython_adafruitio with an esp32spi_socket import time +from os import getenv + +import adafruit_connection_manager +import adafruit_requests import board import busio +from adafruit_esp32spi import adafruit_esp32spi from analogio import AnalogIn from digitalio import DigitalInOut -import adafruit_esp32spi.adafruit_esp32spi_socket as socket -from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests + from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -39,20 +39,16 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) - -socket.set_interface(esp) -requests.set_socket(socket, esp) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) diff --git a/examples/adafruit_io_http/adafruit_io_batch_cpython.py b/examples/adafruit_io_http/adafruit_io_batch_cpython.py new file mode 100644 index 0000000..af45d8d --- /dev/null +++ b/examples/adafruit_io_http/adafruit_io_batch_cpython.py @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: 2024 Tyeth Gundry for Adafruit Industries +# SPDX-License-Identifier: MIT + +# adafruit_circuitpython_adafruitio usage for batch data with a CPython socket. +import datetime +import socket +import ssl +from os import getenv +from random import randint + +import adafruit_requests + +from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError + +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") + +requests = adafruit_requests.Session(socket, ssl.create_default_context()) +# Initialize an Adafruit IO HTTP API object +io = IO_HTTP(aio_username, aio_key, requests) + +try: + # Get the 'temperature' feed from Adafruit IO + temperature_feed = io.get_feed("batch-temperature") +except AdafruitIO_RequestError: + # If no 'temperature' feed exists, create one + temperature_feed = io.create_new_feed("batch-temperature") + +# Get current time from Adafruit IO time service (in UTC) +years, months, days, hours, minutes, seconds, *_ = io.receive_time("UTC") +current_time = datetime.datetime(years, months, days, hours, minutes, seconds) +print("Current time from Adafruit IO: ", current_time) + +# Create random values at different timestamps to send to the feed +data = [] +for i in range(5): + random_value = randint(0, 50) + time_offset = i - 5 + created_at = current_time + datetime.timedelta(seconds=time_offset) + print( + f"Adding datapoint {random_value} (at T:{time_offset}) to collection for batch-temperature feed..." # noqa: E501 + ) + data.append( + { + "value": random_value, + "created_at": created_at.isoformat(), # optional metadata like lat, lon, ele, etc + } + ) + +# Send the data to the feed as a single batch +io.send_batch_data(temperature_feed["key"], data) +print("Data sent!") +print() +print( + "View your feed graph at: https://io.adafruit.com/{0}/feeds/{1}".format( + aio_username, temperature_feed["key"] + ) +) +print() + +# Retrieve data value from the feed +print("Retrieving data from batch-temperature feed...") +received_data = io.receive_data(temperature_feed["key"]) +print("Data from temperature feed: ", received_data["value"]) diff --git a/examples/adafruit_io_http/adafruit_io_create_and_get_feed.py b/examples/adafruit_io_http/adafruit_io_create_and_get_feed.py index 7b687f6..640b838 100644 --- a/examples/adafruit_io_http/adafruit_io_create_and_get_feed.py +++ b/examples/adafruit_io_http/adafruit_io_create_and_get_feed.py @@ -4,44 +4,38 @@ Example using create_and_get_feed. Creates a new feed if it does not exist and sends to it, or sends to an existing feed once it has been created. """ -import ssl + +from os import getenv + +import adafruit_connection_manager import adafruit_requests -import socketpool -import wifi import microcontroller +import wifi + from adafruit_io.adafruit_io import IO_HTTP -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials, and "aio_username" and "aio_key" keys with your -# Adafruit IO credentials, DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print( - "WiFi and Adafruit IO credentials are kept in secrets.py, please add them there!" - ) - raise - -# Connect to Wi-Fi using credentials from secrets.py -wifi.radio.connect(secrets["ssid"], secrets["password"]) -print("Connected to {}!".format(secrets["ssid"])) -print("IP:", wifi.radio.ipv4_address) +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") -pool = socketpool.SocketPool(wifi.radio) -requests = adafruit_requests.Session(pool, ssl.create_default_context()) +# Connect to Wi-Fi using credentials from settings.toml +wifi.radio.connect(ssid, password) +print(f"Connected to {ssid}!") +print("IP:", wifi.radio.ipv4_address) -# Obtain Adafruit IO credentials from secrets.py -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) # Create temperature variable using the CPU temperature and print the current value. temperature = microcontroller.cpu.temperature -print("Current CPU temperature: {0} C".format(temperature)) +print(f"Current CPU temperature: {temperature} C") # Create and get feed. io.send_data(io.create_and_get_feed("cpu-temperature-feed")["key"], temperature) diff --git a/examples/adafruit_io_http/adafruit_io_digital_out.py b/examples/adafruit_io_http/adafruit_io_digital_out.py index c8b1d82..f5e3492 100644 --- a/examples/adafruit_io_http/adafruit_io_digital_out.py +++ b/examples/adafruit_io_http/adafruit_io_digital_out.py @@ -4,23 +4,23 @@ # Turn on and off a LED from your Adafruit IO Dashboard. # adafruit_circuitpython_adafruitio with an esp32spi_socket import time +from os import getenv + +import adafruit_connection_manager +import adafruit_requests import board import busio -from digitalio import DigitalInOut, Direction -import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests +from digitalio import DigitalInOut, Direction + from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -38,20 +38,16 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) - -socket.set_interface(esp) -requests.set_socket(socket, esp) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) diff --git a/examples/adafruit_io_http/adafruit_io_feeds.py b/examples/adafruit_io_http/adafruit_io_feeds.py index 160a540..66aadb1 100644 --- a/examples/adafruit_io_http/adafruit_io_feeds.py +++ b/examples/adafruit_io_http/adafruit_io_feeds.py @@ -4,23 +4,23 @@ # Adafruit IO HTTP API - Feed Interactions # Documentation: https://io.adafruit.com/api/docs/#feeds # adafruit_circuitpython_adafruitio with an esp32spi_socket +from os import getenv + +import adafruit_connection_manager +import adafruit_requests import board import busio -from digitalio import DigitalInOut -import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_HTTP -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -38,20 +38,16 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) - -socket.set_interface(esp) -requests.set_socket(socket, esp) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) diff --git a/examples/adafruit_io_http/adafruit_io_groups.py b/examples/adafruit_io_http/adafruit_io_groups.py index daefd1e..547eaba 100644 --- a/examples/adafruit_io_http/adafruit_io_groups.py +++ b/examples/adafruit_io_http/adafruit_io_groups.py @@ -4,24 +4,24 @@ # Adafruit IO HTTP API - Group Interactions # Documentation: https://io.adafruit.com/api/docs/#groups # adafruit_circuitpython_adafruitio with an esp32spi_socket +from os import getenv + +import adafruit_connection_manager +import adafruit_datetime as datetime +import adafruit_requests import board import busio -from digitalio import DigitalInOut -import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests -from adafruit_io.adafruit_io import IO_HTTP +from digitalio import DigitalInOut +from adafruit_io.adafruit_io import IO_HTTP -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -39,20 +39,26 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) + +# If you are using a wifi based mcu use this instead of esp code above, remove the from +# adafruit_esp32spi import line, optionally esp.connect(ssid, password) +# import wifi +# esp = wifi.radio -socket.set_interface(esp) -requests.set_socket(socket, esp) +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# If you are testing on python with blinka, use real requests below and comment out above: +# import os, datetime, requests as real_requests +# from adafruit_io.adafruit_io import IO_HTTP +# requests = real_requests.Session() # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) @@ -61,13 +67,53 @@ print("Creating a new Adafruit IO Group...") sensor_group = io.create_new_group("envsensors", "a group of environmental sensors") -# Add the 'temperature' feed to the group -print("Adding feed temperature to group...") -io.add_feed_to_group(sensor_group["key"], "temperature") +# Create the 'temperature' feed in the group +print("Creating feed temperature inside group...") +io.create_feed_in_group(sensor_group["key"], "temperature") + +# Create the 'humidity' feed then add to group (it will still be in Default group too) +print("Creating feed humidity then adding to group...") +humidity_feed = io.create_new_feed("humidity", "a feed for humidity data") +io.add_feed_to_group(sensor_group["key"], humidity_feed["key"]) + +# show humidity feed is in two groups +print("Getting fresh humidity feed info... (notice groups)") +print(io.get_feed(humidity_feed["key"])) + +# fetch current time +print("Fetching current time from IO... ", end="") +year, month, day, hour, minute, second, *_ = io.receive_time(timezone="UTC") +old_time = datetime.datetime(year, month, day, hour, minute, second) +print(old_time.isoformat()) + +# Publish data for multiple feeds to a group, use different timestamps for no reason +print("Publishing batch data to group feeds with created_at set 99minutes ago...") +thetime = old_time - datetime.timedelta(minutes=99) +print(thetime) + +io.send_group_data( + group_key=sensor_group["key"], + feeds_and_data=[ + {"key": "temperature", "value": 20.0}, + {"key": "humidity", "value": 40.0}, + ], + metadata={ + "lat": 50.1858942, + "lon": -4.9677478, + "ele": 4, + "created_at": thetime.isoformat(), + }, +) # Get info from the group +print("Getting fresh group info... (notice created_at vs updated_at)") +sensor_group = io.get_group("envsensors") # refresh data via HTTP API print(sensor_group) # Delete the group print("Deleting group...") io.delete_group("envsensors") + +# Delete the remaining humidity feed (it was in Two Groups so not deleted with our group) +print("Deleting feed humidity (still in default group)...") +io.delete_feed(humidity_feed["key"]) diff --git a/examples/adafruit_io_http/adafruit_io_metadata.py b/examples/adafruit_io_http/adafruit_io_metadata.py index 7bfb933..2acc281 100644 --- a/examples/adafruit_io_http/adafruit_io_metadata.py +++ b/examples/adafruit_io_http/adafruit_io_metadata.py @@ -3,23 +3,23 @@ # Adafruit IO HTTP API - Sending values with optional metadata # adafruit_circuitpython_adafruitio with an esp32spi_socket +from os import getenv + +import adafruit_connection_manager +import adafruit_requests import board import busio -from digitalio import DigitalInOut -import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -37,20 +37,16 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) - -socket.set_interface(esp) -requests.set_socket(socket, esp) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) diff --git a/examples/adafruit_io_http/adafruit_io_randomizer.py b/examples/adafruit_io_http/adafruit_io_randomizer.py index d68f2e5..83e4aad 100644 --- a/examples/adafruit_io_http/adafruit_io_randomizer.py +++ b/examples/adafruit_io_http/adafruit_io_randomizer.py @@ -4,23 +4,23 @@ # Example for using Adafruit IO's random data (randomizer) service # adafruit_circuitpython_adafruitio with an esp32spi_socket import time +from os import getenv + +import adafruit_connection_manager +import adafruit_requests import board import busio -from digitalio import DigitalInOut -import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_HTTP -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -38,20 +38,16 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) - -socket.set_interface(esp) -requests.set_socket(socket, esp) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) diff --git a/examples/adafruit_io_http/adafruit_io_simpletest.py b/examples/adafruit_io_http/adafruit_io_simpletest.py index 836e0be..9f93625 100644 --- a/examples/adafruit_io_http/adafruit_io_simpletest.py +++ b/examples/adafruit_io_http/adafruit_io_simpletest.py @@ -2,36 +2,29 @@ # SPDX-License-Identifier: MIT # adafruit_circuitpython_adafruitio usage with native wifi networking -import ssl +from os import getenv from random import randint + +import adafruit_connection_manager import adafruit_requests -import socketpool import wifi -from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise - -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError -print("Connecting to %s" % secrets["ssid"]) -wifi.radio.connect(secrets["ssid"], secrets["password"]) -print("Connected to %s!" % secrets["ssid"]) +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") +print("Connecting to %s" % ssid) +wifi.radio.connect(ssid, password) +print("Connected to %s!" % ssid) -pool = socketpool.SocketPool(wifi.radio) -requests = adafruit_requests.Session(pool, ssl.create_default_context()) +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) @@ -44,7 +37,7 @@ # Send random integer values to the feed random_value = randint(0, 50) -print("Sending {0} to temperature feed...".format(random_value)) +print(f"Sending {random_value} to temperature feed...") io.send_data(temperature_feed["key"], random_value) print("Data sent!") diff --git a/examples/adafruit_io_http/adafruit_io_simpletest_cpython.py b/examples/adafruit_io_http/adafruit_io_simpletest_cpython.py index 52f5031..d0ee4ce 100644 --- a/examples/adafruit_io_http/adafruit_io_simpletest_cpython.py +++ b/examples/adafruit_io_http/adafruit_io_simpletest_cpython.py @@ -4,26 +4,19 @@ # adafruit_circuitpython_adafruitio usage with a CPython socket import socket import ssl +from os import getenv from random import randint -import adafruit_requests -from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +import adafruit_requests -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") requests = adafruit_requests.Session(socket, ssl.create_default_context()) # Initialize an Adafruit IO HTTP API object @@ -38,7 +31,7 @@ # Send random integer values to the feed random_value = randint(0, 50) -print("Sending {0} to temperature feed...".format(random_value)) +print(f"Sending {random_value} to temperature feed...") io.send_data(temperature_feed["key"], random_value) print("Data sent!") diff --git a/examples/adafruit_io_http/adafruit_io_simpletest_esp32spi.py b/examples/adafruit_io_http/adafruit_io_simpletest_esp32spi.py index fb80f1a..27c4ce6 100644 --- a/examples/adafruit_io_http/adafruit_io_simpletest_esp32spi.py +++ b/examples/adafruit_io_http/adafruit_io_simpletest_esp32spi.py @@ -2,24 +2,24 @@ # SPDX-License-Identifier: MIT # adafruit_circuitpython_adafruitio usage with an esp32spi_socket +from os import getenv from random import randint + +import adafruit_connection_manager +import adafruit_requests import board import busio -from digitalio import DigitalInOut -import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -37,20 +37,16 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) - -socket.set_interface(esp) -requests.set_socket(socket, esp) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) @@ -64,7 +60,7 @@ # Send random integer values to the feed random_value = randint(0, 50) -print("Sending {0} to temperature feed...".format(random_value)) +print(f"Sending {random_value} to temperature feed...") io.send_data(temperature_feed["key"], random_value) print("Data sent!") diff --git a/examples/adafruit_io_http/adafruit_io_temperature.py b/examples/adafruit_io_http/adafruit_io_temperature.py index b779a50..85508e1 100644 --- a/examples/adafruit_io_http/adafruit_io_temperature.py +++ b/examples/adafruit_io_http/adafruit_io_temperature.py @@ -4,24 +4,24 @@ # Example of sending ADT7410 sensor temperature values to IO # adafruit_circuitpython_adafruitio with an esp32spi_socket import time +from os import getenv + +import adafruit_adt7410 +import adafruit_connection_manager +import adafruit_requests import board import busio -from digitalio import DigitalInOut -import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests -import adafruit_adt7410 +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -39,20 +39,16 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) - -socket.set_interface(esp) -requests.set_socket(socket, esp) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) @@ -74,7 +70,7 @@ # set temperature value to two precision points temperature = "%0.2f" % (temperature) - print("Current Temperature: {0}*C".format(temperature)) + print(f"Current Temperature: {temperature}*C") print("Sending to Adafruit IO...") io.send_data(temperature_feed["key"], temperature) print("Data sent!") diff --git a/examples/adafruit_io_http/adafruit_io_user_info.py b/examples/adafruit_io_http/adafruit_io_user_info.py new file mode 100644 index 0000000..096f809 --- /dev/null +++ b/examples/adafruit_io_http/adafruit_io_user_info.py @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2024 Tyeth Gundry for Adafruit Industries +# SPDX-License-Identifier: MIT + +# retrieve user rate info via adafruit_circuitpython_adafruitio with native wifi networking +import time +from os import getenv + +import adafruit_connection_manager +import adafruit_requests +import wifi + +from adafruit_io.adafruit_io import IO_HTTP + +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") + +print("Connecting to %s" % ssid) +wifi.radio.connect(ssid, password) +print("Connected to %s!" % ssid) + +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) +requests = adafruit_requests.Session(pool, ssl_context) +# Initialize an Adafruit IO HTTP API object +io = IO_HTTP(aio_username, aio_key, requests) + +print("===============\nUser Rate info:\n===============") +print("\n".join([f"{k:<30}\t=\t{v}" for (k, v) in io.get_user_rate_info().items()])) + +print(f"Throttle limit: {io.get_throttle_limit()}") +print(f"Remaining throttle limit: {io.get_remaining_throttle_limit()}") + + +# # Uncomment these lines to retrieve all user info as one big json object: +# print("Waiting 5seconds before fetching full user info (a lot of JSON output)") +# time.sleep(5) +# try: +# print("\n\nFull User info:") +# print(io.get_user_info()) +# except MemoryError as me: +# print( +# "Board ran out of memory when processing all that user info json." +# + "This is expected on most boards (ESP32-S3 should work)" +# ) +# raise me +# except Exception as e: +# print("Unexpected error!") +# raise e + +print("\n\nDone!") diff --git a/examples/adafruit_io_http/adafruit_io_weather.py b/examples/adafruit_io_http/adafruit_io_weather.py index e65c654..4ee1a03 100644 --- a/examples/adafruit_io_http/adafruit_io_weather.py +++ b/examples/adafruit_io_http/adafruit_io_weather.py @@ -3,23 +3,23 @@ # Example of using the Adafruit IO+ Weather Service # adafruit_circuitpython_adafruitio with an esp32spi_socket +from os import getenv + +import adafruit_connection_manager +import adafruit_requests import board import busio -from digitalio import DigitalInOut -import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi -import adafruit_requests as requests +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_HTTP -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -37,20 +37,16 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(ssid, password) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue -print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) +print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi) -socket.set_interface(esp) -requests.set_socket(socket, esp) - -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) +requests = adafruit_requests.Session(pool, ssl_context) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) @@ -68,11 +64,7 @@ # Get today's forecast current_forecast = forecast["current"] -print( - "It is {0} and {1}*F.".format( - current_forecast["summary"], current_forecast["temperature"] - ) -) +print("It is {0} and {1}*F.".format(current_forecast["summary"], current_forecast["temperature"])) print("with a humidity of {0}%".format(current_forecast["humidity"] * 100)) # Get tomorrow's forecast diff --git a/examples/adafruit_io_mqtt/adafruit_io_feed_callback.py b/examples/adafruit_io_mqtt/adafruit_io_feed_callback.py index 2f083dc..b1e286c 100644 --- a/examples/adafruit_io_mqtt/adafruit_io_feed_callback.py +++ b/examples/adafruit_io_mqtt/adafruit_io_feed_callback.py @@ -2,25 +2,26 @@ # SPDX-License-Identifier: MIT import time +from os import getenv +import adafruit_connection_manager +import adafruit_minimqtt.adafruit_minimqtt as MQTT import board import busio -from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi -from adafruit_esp32spi import adafruit_esp32spi_wifimanager -import adafruit_esp32spi.adafruit_esp32spi_socket as socket import neopixel -import adafruit_minimqtt.adafruit_minimqtt as MQTT +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_MQTT -### WiFi ### +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") -# 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 +### WiFi ### # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -35,23 +36,20 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) """Use below for Most Boards""" -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2 -) # Uncomment for Most Boards +status_pixel = 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) +# status_pixel = 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) +# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel) # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. # This is a good place to subscribe to feed changes. The client parameter @@ -62,31 +60,37 @@ def connected(client): def subscribe(client, userdata, topic, granted_qos): # This method is called when the client subscribes to a new feed. - print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos)) + print(f"Subscribed to {topic} with QOS level {granted_qos}") def unsubscribe(client, userdata, topic, pid): # This method is called when the client unsubscribes from a feed. - print("Unsubscribed from {0} with PID {1}".format(topic, pid)) + print(f"Unsubscribed from {topic} with PID {pid}") -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") -# pylint: disable=unused-argument +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) + + def on_message(client, feed_id, payload): # Message function will be called when a subscribed feed has a new value. # The feed_id parameter identifies the feed, and the payload parameter has # the new value. - print("Feed {0} received new value: {1}".format(feed_id, payload)) + print(f"Feed {feed_id} received new value: {payload}") def on_battery_msg(client, topic, message): # Method called whenever user/feeds/battery has a new value - print("Battery level: {}v".format(message)) + print(f"Battery level: {message}v") # Connect to WiFi @@ -94,15 +98,17 @@ def on_battery_msg(client, topic, message): wifi.connect() print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=1883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Initialize an Adafruit IO MQTT Client @@ -114,6 +120,7 @@ def on_battery_msg(client, topic, message): io.on_subscribe = subscribe io.on_unsubscribe = unsubscribe io.on_message = on_message +io.on_publish = publish # Connect to Adafruit IO print("Connecting to Adafruit IO...") diff --git a/examples/adafruit_io_mqtt/adafruit_io_groups.py b/examples/adafruit_io_mqtt/adafruit_io_groups.py index 753c263..39a3894 100755 --- a/examples/adafruit_io_mqtt/adafruit_io_groups.py +++ b/examples/adafruit_io_mqtt/adafruit_io_groups.py @@ -4,26 +4,27 @@ # Subscribing to an Adafruit IO Group # and Publishing to the feeds in the group import time +from os import getenv from random import randint +import adafruit_connection_manager +import adafruit_minimqtt.adafruit_minimqtt as MQTT import board import busio -from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi -from adafruit_esp32spi import adafruit_esp32spi_wifimanager -import adafruit_esp32spi.adafruit_esp32spi_socket as socket import neopixel -import adafruit_minimqtt.adafruit_minimqtt as MQTT +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_MQTT -### WiFi ### +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") -# 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 +### WiFi ### # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -38,23 +39,20 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) """Use below for Most Boards""" -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2 -) # Uncomment for Most Boards +status_pixel = 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) +# status_pixel = 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) +# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel) # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. # This is a good place to subscribe to feed changes. The client parameter @@ -66,18 +64,24 @@ def connected(client): io.subscribe(group_key=group_name) -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") -# pylint: disable=unused-argument +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) + + def message(client, feed_id, payload): # Message function will be called when a subscribed feed has a new value. # The feed_id parameter identifies the feed, and the payload parameter has # the new value. - print("Feed {0} received new value: {1}".format(feed_id, payload)) + print(f"Feed {feed_id} received new value: {payload}") # Connect to WiFi @@ -85,15 +89,17 @@ def message(client, feed_id, payload): wifi.connect() print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=1883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Initialize an Adafruit IO MQTT Client @@ -103,6 +109,7 @@ def message(client, feed_id, payload): io.on_connect = connected io.on_disconnect = disconnected io.on_message = message +io.on_publish = publish # Group name group_name = "weatherstation" @@ -120,10 +127,10 @@ def message(client, feed_id, payload): while True: io.loop() temp_reading = randint(0, 100) - print("Publishing value {0} to feed: {1}".format(temp_reading, temp_feed)) + print(f"Publishing value {temp_reading} to feed: {temp_feed}") io.publish(temp_feed, temp_reading) humid_reading = randint(0, 100) - print("Publishing value {0} to feed: {1}".format(humid_reading, humid_feed)) + print(f"Publishing value {humid_reading} to feed: {humid_feed}") io.publish(humid_feed, humid_reading) time.sleep(5) diff --git a/examples/adafruit_io_mqtt/adafruit_io_location.py b/examples/adafruit_io_mqtt/adafruit_io_location.py index 09c9291..9b17824 100755 --- a/examples/adafruit_io_mqtt/adafruit_io_location.py +++ b/examples/adafruit_io_mqtt/adafruit_io_location.py @@ -4,26 +4,26 @@ # Example of tagging data with location values # and sending it to an Adafruit IO feed. import time +from os import getenv + +import adafruit_connection_manager +import adafruit_minimqtt.adafruit_minimqtt as MQTT import board import busio -from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi -from adafruit_esp32spi import adafruit_esp32spi_wifimanager -import adafruit_esp32spi.adafruit_esp32spi_socket as socket import neopixel +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +from digitalio import DigitalInOut - -import adafruit_minimqtt.adafruit_minimqtt as MQTT from adafruit_io.adafruit_io import IO_MQTT -### WiFi ### +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") -# 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 +### WiFi ### # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -38,23 +38,20 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) """Use below for Most Boards""" -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2 -) # Uncomment for Most Boards +status_pixel = 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) +# status_pixel = 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) +# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel) # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. # This is a good place to subscribe to feed changes. The client parameter @@ -66,18 +63,24 @@ def connected(client): io.subscribe("location") -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") -# pylint: disable=unused-argument +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) + + def message(client, feed_id, payload): # Message function will be called when a subscribed feed has a new value. # The feed_id parameter identifies the feed, and the payload parameter has # the new value. - print("Feed {0} received new value: {1}".format(feed_id, payload)) + print(f"Feed {feed_id} received new value: {payload}") # Connect to WiFi @@ -85,15 +88,17 @@ def message(client, feed_id, payload): wifi.connect() print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=1883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Initialize an Adafruit IO MQTT Client @@ -103,6 +108,7 @@ def message(client, feed_id, payload): io.on_connect = connected io.on_disconnect = disconnected io.on_message = message +io.on_publish = publish # Connect to Adafruit IO io.connect() diff --git a/examples/adafruit_io_mqtt/adafruit_io_pubsub_rp2040.py b/examples/adafruit_io_mqtt/adafruit_io_pubsub_rp2040.py index db15278..6792d05 100644 --- a/examples/adafruit_io_mqtt/adafruit_io_pubsub_rp2040.py +++ b/examples/adafruit_io_mqtt/adafruit_io_pubsub_rp2040.py @@ -2,24 +2,26 @@ # SPDX-License-Identifier: MIT import time -from microcontroller import cpu +from os import getenv + +import adafruit_connection_manager +import adafruit_minimqtt.adafruit_minimqtt as MQTT import board import busio +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi -from adafruit_esp32spi import adafruit_esp32spi_wifimanager -import adafruit_esp32spi.adafruit_esp32spi_socket as socket -import adafruit_minimqtt.adafruit_minimqtt as MQTT +from microcontroller import cpu + from adafruit_io.adafruit_io import IO_MQTT -### WiFi ### +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") -# 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 +### WiFi ### # Raspberry Pi RP2040 esp32_cs = DigitalInOut(board.GP13) @@ -29,7 +31,7 @@ spi = busio.SPI(board.GP10, board.GP11, board.GP12) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) -wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password) # Configure the RP2040 Pico LED Pin as an output led_pin = DigitalInOut(board.LED) @@ -37,7 +39,6 @@ # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. print("Connected to Adafruit IO! ") @@ -45,10 +46,17 @@ def connected(client): def subscribe(client, userdata, topic, granted_qos): # This method is called when the client subscribes to a new feed. - print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos)) + print(f"Subscribed to {topic} with QOS level {granted_qos}") + + +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") @@ -56,7 +64,7 @@ def disconnected(client): def on_led_msg(client, topic, message): # Method called whenever user/feeds/led has a new value - print("New message on topic {0}: {1} ".format(topic, message)) + print(f"New message on topic {topic}: {message} ") if message == "ON": led_pin.value = True elif message == "OFF": @@ -70,15 +78,17 @@ def on_led_msg(client, topic, message): wifi.connect() print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=1883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Initialize an Adafruit IO MQTT Client @@ -88,6 +98,7 @@ def on_led_msg(client, topic, message): io.on_connect = connected io.on_disconnect = disconnected io.on_subscribe = subscribe +io.on_publish = publish # Set up a callback for the led feed io.add_feed_callback("led", on_led_msg) diff --git a/examples/adafruit_io_mqtt/adafruit_io_simpletest_cellular.py b/examples/adafruit_io_mqtt/adafruit_io_simpletest_cellular.py index 1514e37..63d4e19 100644 --- a/examples/adafruit_io_mqtt/adafruit_io_simpletest_cellular.py +++ b/examples/adafruit_io_mqtt/adafruit_io_simpletest_cellular.py @@ -6,25 +6,27 @@ # to subscribe to an Adafruit IO feed and publish random data # to be received by the feed. import time +from os import getenv from random import randint +import adafruit_connection_manager +import adafruit_fona.adafruit_fona_socket as pool +import adafruit_minimqtt.adafruit_minimqtt as MQTT import board import busio import digitalio - from adafruit_fona.adafruit_fona import FONA from adafruit_fona.adafruit_fona_gsm import GSM -import adafruit_fona.adafruit_fona_socket as cellular_socket -import adafruit_minimqtt.adafruit_minimqtt as MQTT from adafruit_io.adafruit_io import IO_MQTT -# Get MQTT details and more from a secrets.py file -try: - from secrets import secrets -except ImportError: - print("MQTT secrets are kept in secrets.py, please add them there!") - raise +# Get Fona details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +apn = getenv("apn") +apn_username = getenv("apn_username") +apn_password = getenv("apn_password") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") # Create a serial connection for the FONA connection uart = busio.UART(board.TX, board.RX) @@ -34,7 +36,7 @@ fona = FONA(uart, rst) # initialize gsm -gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) +gsm = GSM(fona, (apn, apn_username, apn_password)) while not gsm.is_attached: print("Attaching to network...") @@ -47,7 +49,6 @@ # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. # This is a good place to subscribe to feed changes. The client parameter @@ -60,37 +61,44 @@ def connected(client): def subscribe(client, userdata, topic, granted_qos): # This method is called when the client subscribes to a new feed. - print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos)) + print(f"Subscribed to {topic} with QOS level {granted_qos}") def unsubscribe(client, userdata, topic, pid): # This method is called when the client unsubscribes from a feed. - print("Unsubscribed from {0} with PID {1}".format(topic, pid)) + print(f"Unsubscribed from {topic} with PID {pid}") -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") -# pylint: disable=unused-argument +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) + + def message(client, feed_id, payload): # Message function will be called when a subscribed feed has a new value. # The feed_id parameter identifies the feed, and the payload parameter has # the new value. - print("Feed {0} received new value: {1}".format(feed_id, payload)) + print(f"Feed {feed_id} received new value: {payload}") -# Initialize MQTT interface with the ethernet interface -MQTT.set_socket(cellular_socket, fona) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, fona) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=1883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Initialize an Adafruit IO MQTT Client @@ -102,6 +110,7 @@ def message(client, feed_id, payload): io.on_subscribe = subscribe io.on_unsubscribe = unsubscribe io.on_message = message +io.on_publish = publish # Connect to Adafruit IO print("Connecting to Adafruit IO...") @@ -116,6 +125,6 @@ def message(client, feed_id, payload): # Send a new message every 10 seconds. if (time.monotonic() - last) >= 5: value = randint(0, 100) - print("Publishing {0} to DemoFeed.".format(value)) + print(f"Publishing {value} to DemoFeed.") io.publish("DemoFeed", value) last = time.monotonic() diff --git a/examples/adafruit_io_mqtt/adafruit_io_simpletest_esp32s2.py b/examples/adafruit_io_mqtt/adafruit_io_simpletest_esp32s2.py index 1ecb026..d60daf7 100644 --- a/examples/adafruit_io_mqtt/adafruit_io_simpletest_esp32s2.py +++ b/examples/adafruit_io_mqtt/adafruit_io_simpletest_esp32s2.py @@ -1,51 +1,31 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT import time +from os import getenv from random import randint -import os -import ssl -import socketpool -import wifi +import adafruit_connection_manager import adafruit_minimqtt.adafruit_minimqtt as MQTT +import wifi + from adafruit_io.adafruit_io import IO_MQTT -### WiFi ### +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - if os.getenv("AIO_USERNAME") and os.getenv("AIO_KEY"): - secrets = { - "aio_username": os.getenv("AIO_USERNAME"), - "aio_key": os.getenv("AIO_KEY"), - "ssid": os.getenv("CIRCUITPY_WIFI_SSID"), - "password": os.getenv("CIRCUITPY_WIFI_PASSWORD"), - } - else: - from secrets import secrets -except ImportError: - print( - "WiFi + Adafruit IO secrets are kept in secrets.py or settings.toml, please add them there!" - ) - raise - -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +### WiFi ### if not wifi.radio.connected: - print("Connecting to %s" % secrets["ssid"]) - wifi.radio.connect(secrets["ssid"], secrets["password"]) - print("Connected to %s!" % secrets["ssid"]) + print("Connecting to %s" % ssid) + wifi.radio.connect(ssid, password) + print("Connected to %s!" % ssid) # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. # This is a good place to subscribe to feed changes. The client parameter @@ -58,39 +38,46 @@ def connected(client): def subscribe(client, userdata, topic, granted_qos): # This method is called when the client subscribes to a new feed. - print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos)) + print(f"Subscribed to {topic} with QOS level {granted_qos}") def unsubscribe(client, userdata, topic, pid): # This method is called when the client unsubscribes from a feed. - print("Unsubscribed from {0} with PID {1}".format(topic, pid)) + print(f"Unsubscribed from {topic} with PID {pid}") -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") -# pylint: disable=unused-argument +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) + + def message(client, feed_id, payload): # Message function will be called when a subscribed feed has a new value. # The feed_id parameter identifies the feed, and the payload parameter has # the new value. - print("Feed {0} received new value: {1}".format(feed_id, payload)) + print(f"Feed {feed_id} received new value: {payload}") -# Create a socket pool -pool = socketpool.SocketPool(wifi.radio) +# Create a socket pool and ssl_context +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=8883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, socket_pool=pool, - ssl_context=ssl.create_default_context(), + ssl_context=ssl_context, is_ssl=True, ) @@ -103,6 +90,7 @@ def message(client, feed_id, payload): io.on_subscribe = subscribe io.on_unsubscribe = unsubscribe io.on_message = message +io.on_publish = publish # Connect to Adafruit IO print("Connecting to Adafruit IO...") @@ -117,6 +105,6 @@ def message(client, feed_id, payload): # Send a new message every 10 seconds. if (time.monotonic() - last) >= 5: value = randint(0, 100) - print("Publishing {0} to DemoFeed.".format(value)) + print(f"Publishing {value} to DemoFeed.") io.publish("DemoFeed", value) last = time.monotonic() diff --git a/examples/adafruit_io_mqtt/adafruit_io_simpletest_eth.py b/examples/adafruit_io_mqtt/adafruit_io_simpletest_eth.py index d0dee85..63dbd54 100755 --- a/examples/adafruit_io_mqtt/adafruit_io_simpletest_eth.py +++ b/examples/adafruit_io_mqtt/adafruit_io_simpletest_eth.py @@ -6,23 +6,24 @@ # to subscribe to an Adafruit IO feed and publish random data # to be received by the feed. import time +from os import getenv from random import randint +import adafruit_connection_manager +import adafruit_minimqtt.adafruit_minimqtt as MQTT import board import busio +from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K from digitalio import DigitalInOut -from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K -import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket -import adafruit_minimqtt.adafruit_minimqtt as MQTT from adafruit_io.adafruit_io import IO_MQTT -# Get MQTT details and more from a secrets.py file -try: - from secrets import secrets -except ImportError: - print("MQTT secrets are kept in secrets.py, please add them there!") - raise +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") cs = DigitalInOut(board.D10) spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) @@ -32,7 +33,6 @@ # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. # This is a good place to subscribe to feed changes. The client parameter @@ -45,37 +45,45 @@ def connected(client): def subscribe(client, userdata, topic, granted_qos): # This method is called when the client subscribes to a new feed. - print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos)) + print(f"Subscribed to {topic} with QOS level {granted_qos}") def unsubscribe(client, userdata, topic, pid): # This method is called when the client unsubscribes from a feed. - print("Unsubscribed from {0} with PID {1}".format(topic, pid)) + print(f"Unsubscribed from {topic} with PID {pid}") -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") -# pylint: disable=unused-argument +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) + + def message(client, feed_id, payload): # Message function will be called when a subscribed feed has a new value. # The feed_id parameter identifies the feed, and the payload parameter has # the new value. - print("Feed {0} received new value: {1}".format(feed_id, payload)) + print(f"Feed {feed_id} received new value: {payload}") -# Initialize MQTT interface with the ethernet interface -MQTT.set_socket(socket, eth) +pool = adafruit_connection_manager.get_radio_socketpool(eth) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(eth) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=1883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Initialize an Adafruit IO MQTT Client @@ -87,6 +95,7 @@ def message(client, feed_id, payload): io.on_subscribe = subscribe io.on_unsubscribe = unsubscribe io.on_message = message +io.on_publish = publish # Connect to Adafruit IO print("Connecting to Adafruit IO...") @@ -101,6 +110,6 @@ def message(client, feed_id, payload): # Send a new message every 10 seconds. if (time.monotonic() - last) >= 5: value = randint(0, 100) - print("Publishing {0} to DemoFeed.".format(value)) + print(f"Publishing {value} to DemoFeed.") io.publish("DemoFeed", value) last = time.monotonic() diff --git a/examples/adafruit_io_mqtt/adafruit_io_time.py b/examples/adafruit_io_mqtt/adafruit_io_time.py index 2fe544c..43eaf87 100755 --- a/examples/adafruit_io_mqtt/adafruit_io_time.py +++ b/examples/adafruit_io_mqtt/adafruit_io_time.py @@ -5,25 +5,26 @@ # for obtaining the current server time, if you don't have # access to a RTC module. import time +from os import getenv + +import adafruit_connection_manager +import adafruit_minimqtt.adafruit_minimqtt as MQTT import board import busio -from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi -from adafruit_esp32spi import adafruit_esp32spi_wifimanager -import adafruit_esp32spi.adafruit_esp32spi_socket as socket import neopixel +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +from digitalio import DigitalInOut -import adafruit_minimqtt.adafruit_minimqtt as MQTT from adafruit_io.adafruit_io import IO_MQTT -### WiFi ### +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") -# 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 +### WiFi ### # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -38,23 +39,20 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) """Use below for Most Boards""" -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2 -) # Uncomment for Most Boards +status_pixel = 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) +# status_pixel = 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) +# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel) # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. # This is a good place to subscribe to feed changes. The client parameter @@ -80,18 +78,24 @@ def connected(client): io.subscribe_to_time("hours") -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") -# pylint: disable=unused-argument +def publish(client, userdata, topic, pid): + # This method is called when the client publishes data to a feed. + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) + + def message(client, feed_id, payload): # Message function will be called when a subscribed feed has a new value. # The feed_id parameter identifies the feed, and the payload parameter has # the new value. - print("Feed {0} received new value: {1}".format(feed_id, payload)) + print(f"Feed {feed_id} received new value: {payload}") # Connect to WiFi @@ -99,15 +103,17 @@ def message(client, feed_id, payload): wifi.connect() print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=1883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Initialize an Adafruit IO MQTT Client @@ -117,6 +123,7 @@ def message(client, feed_id, payload): io.on_connect = connected io.on_disconnect = disconnected io.on_message = message +io.on_publish = publish # Connect to Adafruit IO io.connect() diff --git a/examples/adafruit_io_simpletest.py b/examples/adafruit_io_simpletest.py index 206cb0d..8c99e2d 100755 --- a/examples/adafruit_io_simpletest.py +++ b/examples/adafruit_io_simpletest.py @@ -6,27 +6,27 @@ # to subscribe to an Adafruit IO feed and publish random data # to be received by the feed. import time +from os import getenv from random import randint - +import adafruit_connection_manager +import adafruit_minimqtt.adafruit_minimqtt as MQTT import board import busio -from digitalio import DigitalInOut -from adafruit_esp32spi import adafruit_esp32spi -from adafruit_esp32spi import adafruit_esp32spi_wifimanager -import adafruit_esp32spi.adafruit_esp32spi_socket as socket import neopixel -import adafruit_minimqtt.adafruit_minimqtt as MQTT +from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager +from digitalio import DigitalInOut + from adafruit_io.adafruit_io import IO_MQTT -### WiFi ### +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("CIRCUITPY_WIFI_SSID") +password = getenv("CIRCUITPY_WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") -# 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 +### WiFi ### # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -41,23 +41,20 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) """Use below for Most Boards""" -status_light = neopixel.NeoPixel( - board.NEOPIXEL, 1, brightness=0.2 -) # Uncomment for Most Boards +status_pixel = 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) +# status_pixel = 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) +# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) +wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel) # Define callback functions which will be called when certain events happen. -# pylint: disable=unused-argument def connected(client): # Connected function will be called when the client is connected to Adafruit IO. # This is a good place to subscribe to feed changes. The client parameter @@ -70,26 +67,32 @@ def connected(client): def subscribe(client, userdata, topic, granted_qos): # This method is called when the client subscribes to a new feed. - print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos)) + print(f"Subscribed to {topic} with QOS level {granted_qos}") def unsubscribe(client, userdata, topic, pid): # This method is called when the client unsubscribes from a feed. - print("Unsubscribed from {0} with PID {1}".format(topic, pid)) + print(f"Unsubscribed from {topic} with PID {pid}") -# pylint: disable=unused-argument def disconnected(client): # Disconnected function will be called when the client disconnects. print("Disconnected from Adafruit IO!") -# pylint: disable=unused-argument +def publish(client, userdata, topic, pid): + """This method is called when the client publishes data to a feed.""" + print(f"Published to {topic} with PID {pid}") + if userdata is not None: + print("Published User data: ", end="") + print(userdata) + + def message(client, feed_id, payload): # Message function will be called when a subscribed feed has a new value. # The feed_id parameter identifies the feed, and the payload parameter has # the new value. - print("Feed {0} received new value: {1}".format(feed_id, payload)) + print(f"Feed {feed_id} received new value: {payload}") # Connect to WiFi @@ -97,15 +100,17 @@ def message(client, feed_id, payload): wifi.connect() print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +pool = adafruit_connection_manager.get_radio_socketpool(esp) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) # Initialize a new MQTT Client object mqtt_client = MQTT.MQTT( broker="io.adafruit.com", port=1883, - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) @@ -118,6 +123,7 @@ def message(client, feed_id, payload): io.on_subscribe = subscribe io.on_unsubscribe = unsubscribe io.on_message = message +io.on_publish = publish # Connect to Adafruit IO print("Connecting to Adafruit IO...") @@ -132,6 +138,6 @@ def message(client, feed_id, payload): # Send a new message every 10 seconds. if (time.monotonic() - last) >= 5: value = randint(0, 100) - print("Publishing {0} to DemoFeed.".format(value)) + print(f"Publishing {value} to DemoFeed.") io.publish("DemoFeed", value) last = time.monotonic() diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..1b887b1 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,107 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +target-version = "py38" +line-length = 100 + +[lint] +preview = true +select = ["I", "PL", "UP"] + +extend-select = [ + "D419", # empty-docstring + "E501", # line-too-long + "W291", # trailing-whitespace + "PLC0414", # useless-import-alias + "PLC2401", # non-ascii-name + "PLC2801", # unnecessary-dunder-call + "PLC3002", # unnecessary-direct-lambda-call + "E999", # syntax-error + "PLE0101", # return-in-init + "F706", # return-outside-function + "F704", # yield-outside-function + "PLE0116", # continue-in-finally + "PLE0117", # nonlocal-without-binding + "PLE0241", # duplicate-bases + "PLE0302", # unexpected-special-method-signature + "PLE0604", # invalid-all-object + "PLE0605", # invalid-all-format + "PLE0643", # potential-index-error + "PLE0704", # misplaced-bare-raise + "PLE1141", # dict-iter-missing-items + "PLE1142", # await-outside-async + "PLE1205", # logging-too-many-args + "PLE1206", # logging-too-few-args + "PLE1307", # bad-string-format-type + "PLE1310", # bad-str-strip-call + "PLE1507", # invalid-envvar-value + "PLE2502", # bidirectional-unicode + "PLE2510", # invalid-character-backspace + "PLE2512", # invalid-character-sub + "PLE2513", # invalid-character-esc + "PLE2514", # invalid-character-nul + "PLE2515", # invalid-character-zero-width-space + "PLR0124", # comparison-with-itself + "PLR0202", # no-classmethod-decorator + "PLR0203", # no-staticmethod-decorator + "UP004", # useless-object-inheritance + "PLR0206", # property-with-parameters + "PLR0904", # too-many-public-methods + "PLR0911", # too-many-return-statements + "PLR0912", # too-many-branches + "PLR0913", # too-many-arguments + "PLR0914", # too-many-locals + "PLR0915", # too-many-statements + "PLR0916", # too-many-boolean-expressions + "PLR1702", # too-many-nested-blocks + "PLR1704", # redefined-argument-from-local + "PLR1711", # useless-return + "C416", # unnecessary-comprehension + "PLR1733", # unnecessary-dict-index-lookup + "PLR1736", # unnecessary-list-index-lookup + + # ruff reports this rule is unstable + #"PLR6301", # no-self-use + + "PLW0108", # unnecessary-lambda + "PLW0120", # useless-else-on-loop + "PLW0127", # self-assigning-variable + "PLW0129", # assert-on-string-literal + "B033", # duplicate-value + "PLW0131", # named-expr-without-context + "PLW0245", # super-without-brackets + "PLW0406", # import-self + "PLW0602", # global-variable-not-assigned + "PLW0603", # global-statement + "PLW0604", # global-at-module-level + + # fails on the try: import typing used by libraries + #"F401", # unused-import + + "F841", # unused-variable + "E722", # bare-except + "PLW0711", # binary-op-exception + "PLW1501", # bad-open-mode + "PLW1508", # invalid-envvar-default + "PLW1509", # subprocess-popen-preexec-fn + "PLW2101", # useless-with-lock + "PLW3301", # nested-min-max +] + +ignore = [ + "PLR2004", # magic-value-comparison + "UP030", # format literals + "PLW1514", # unspecified-encoding + "PLR0913", # too-many-arguments + "PLR0915", # too-many-statements + "PLR0917", # too-many-positional-arguments + "PLR0904", # too-many-public-methods + "PLR0912", # too-many-branches + "PLR0916", # too-many-boolean-expressions + "PLR6301", # could-be-static no-self-use + "PLC0415", # import outside toplevel +] + +[format] +line-ending = "lf" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy