From e0998f0ffd31d11b79ce724a21db4a25d51a8800 Mon Sep 17 00:00:00 2001 From: tyeth Date: Tue, 16 Jul 2024 21:09:32 +0100 Subject: [PATCH 1/5] Add list of dicts support to send_batch_data --- adafruit_io/adafruit_io.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/adafruit_io/adafruit_io.py b/adafruit_io/adafruit_io.py index ea38556..3f385ff 100755 --- a/adafruit_io/adafruit_io.py +++ b/adafruit_io/adafruit_io.py @@ -636,12 +636,17 @@ 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) + 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("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}) + self._post(path, {"data": data_list}) def send_group_data( self, group_key: str, feeds_and_data: list, metadata: Optional[dict] = None From 26c459fee107ef3ccc738eac3d311ab961244785 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Tue, 16 Jul 2024 21:13:24 +0100 Subject: [PATCH 2/5] format whitespace --- adafruit_io/adafruit_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_io/adafruit_io.py b/adafruit_io/adafruit_io.py index 3f385ff..8325b01 100755 --- a/adafruit_io/adafruit_io.py +++ b/adafruit_io/adafruit_io.py @@ -641,7 +641,7 @@ def send_batch_data(self, feed_key: str, data_list: list): validate_feed_key(feed_key) 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 + 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") From 41e858b2e6e8df604c317e1371e958073cd4e996 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 18 Jul 2024 12:27:19 +0100 Subject: [PATCH 3/5] Add batch example --- .../adafruit_io_batch_cpython.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 examples/adafruit_io_http/adafruit_io_batch_cpython.py 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..6249f26 --- /dev/null +++ b/examples/adafruit_io_http/adafruit_io_batch_cpython.py @@ -0,0 +1,82 @@ +# 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 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 "aio_username" +# and "aio_key" entries with your IO credentials, or set environment variables/defaults below. +# *** 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: + import os + + secrets = { + "aio_username": os.getenv("ADAFRUIT_AIO_USERNAME", "Your_Username_Here"), + "aio_key": os.getenv("ADAFRUIT_AIO_KEY", "Your_Adafruit_IO_Key_Here"), + } + if ( + secrets["aio_key"] == "Your_Adafruit_IO_Key_Here" + or secrets["aio_username"] == "Your_Username_Here" + ): + print("Adafruit IO 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"] + + +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( + "Adding datapoint {0} (at T:{1}) to collection for batch-temperature feed...".format( + random_value, time_offset + ) + ) + data.append({"value": random_value, "created_at": created_at.isoformat()}) + +# 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"]) From b85b8e261c67fc022ed9582b6a520ba6a33a087e Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 18 Jul 2024 12:32:23 +0100 Subject: [PATCH 4/5] Adjust line endings for batch example --- .../adafruit_io_batch_cpython.py | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/examples/adafruit_io_http/adafruit_io_batch_cpython.py b/examples/adafruit_io_http/adafruit_io_batch_cpython.py index 6249f26..8c994c4 100644 --- a/examples/adafruit_io_http/adafruit_io_batch_cpython.py +++ b/examples/adafruit_io_http/adafruit_io_batch_cpython.py @@ -1,82 +1,82 @@ -# 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 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 "aio_username" -# and "aio_key" entries with your IO credentials, or set environment variables/defaults below. -# *** 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: - import os - - secrets = { - "aio_username": os.getenv("ADAFRUIT_AIO_USERNAME", "Your_Username_Here"), - "aio_key": os.getenv("ADAFRUIT_AIO_KEY", "Your_Adafruit_IO_Key_Here"), - } - if ( - secrets["aio_key"] == "Your_Adafruit_IO_Key_Here" - or secrets["aio_username"] == "Your_Username_Here" - ): - print("Adafruit IO 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"] - - -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( - "Adding datapoint {0} (at T:{1}) to collection for batch-temperature feed...".format( - random_value, time_offset - ) - ) - data.append({"value": random_value, "created_at": created_at.isoformat()}) - -# 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"]) +# 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 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 "aio_username" +# and "aio_key" entries with your IO credentials, or set environment variables/defaults below. +# *** 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: + import os + + secrets = { + "aio_username": os.getenv("ADAFRUIT_AIO_USERNAME", "Your_Username_Here"), + "aio_key": os.getenv("ADAFRUIT_AIO_KEY", "Your_Adafruit_IO_Key_Here"), + } + if ( + secrets["aio_key"] == "Your_Adafruit_IO_Key_Here" + or secrets["aio_username"] == "Your_Username_Here" + ): + print("Adafruit IO 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"] + + +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( + "Adding datapoint {0} (at T:{1}) to collection for batch-temperature feed...".format( + random_value, time_offset + ) + ) + data.append({"value": random_value, "created_at": created_at.isoformat()}) + +# 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"]) From a83c183f965b593bc3046840474a594df570ba76 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 18 Jul 2024 12:47:56 +0100 Subject: [PATCH 5/5] explain optional metadata arguments --- examples/adafruit_io_http/adafruit_io_batch_cpython.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/adafruit_io_http/adafruit_io_batch_cpython.py b/examples/adafruit_io_http/adafruit_io_batch_cpython.py index 8c994c4..16835a9 100644 --- a/examples/adafruit_io_http/adafruit_io_batch_cpython.py +++ b/examples/adafruit_io_http/adafruit_io_batch_cpython.py @@ -63,7 +63,12 @@ random_value, time_offset ) ) - data.append({"value": random_value, "created_at": created_at.isoformat()}) + 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)