diff --git a/adafruit_io/adafruit_io.py b/adafruit_io/adafruit_io.py index 67b014c..d780c6f 100755 --- a/adafruit_io/adafruit_io.py +++ b/adafruit_io/adafruit_io.py @@ -631,10 +631,34 @@ def send_batch_data(self, feed_key: str, data_list: list): :param list Data: Data list to send """ validate_feed_key(feed_key) - path = "feeds/{0}/data/batch".format(feed_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}) + 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("groups/{0}/data".format(group_key)) + 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): """ Get all data values from a specified Adafruit IO feed. Data is @@ -818,14 +842,21 @@ def receive_random_data(self, generator_id: int): path = self._compose_path("integrations/words/{0}".format(generator_id)) return self._get(path) - def receive_time(self): + 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 += "?tz={0}".format(timezone) time_struct = self._get(path) return time.struct_time( + # pylint: disable=line-too-long ( time_struct["year"], time_struct["mon"], diff --git a/examples/adafruit_io_http/adafruit_io_groups.py b/examples/adafruit_io_http/adafruit_io_groups.py index 7398605..2e1754b 100644 --- a/examples/adafruit_io_http/adafruit_io_groups.py +++ b/examples/adafruit_io_http/adafruit_io_groups.py @@ -4,6 +4,7 @@ # Adafruit IO HTTP API - Group Interactions # Documentation: https://io.adafruit.com/api/docs/#groups # adafruit_circuitpython_adafruitio with an esp32spi_socket +import adafruit_datetime as datetime import board import busio from digitalio import DigitalInOut @@ -14,14 +15,26 @@ # 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. +# "password" keys with your WiFi credentials, along with "aio_username" and "aio_key" for +# your Adafruit IO user/key. 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 os + + if os.getenv("ADAFRUIT_AIO_USERNAME") and os.getenv("ADAFRUIT_AIO_KEY"): + secrets = { + "aio_username": os.getenv("ADAFRUIT_AIO_USERNAME", "Your_Username_Here"), + "aio_key": os.getenv("ADAFRUIT_AIO_KEY", "Your_Adafruit_IO_Key_Here"), + "ssid": os.getenv("CIRCUITPY_WIFI_SSID", ""), + "password": os.getenv("CIRCUITPY_WIFI_PASSWORD", ""), + } + else: + print( + "WiFi + Adafruit IO secrets are kept in secrets.py, please add them there!" + ) + raise # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -45,11 +58,26 @@ continue print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.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(secrets["ssid"], secrets["password"]) +# import wifi +# esp = wifi.radio + # 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) +# 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 +# secrets = { +# "aio_username": os.getenv("ADAFRUIT_AIO_USERNAME"), +# "aio_key": os.getenv("ADAFRUIT_AIO_KEY"), +# } +# requests = real_requests.Session() + + # 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.) @@ -72,8 +100,37 @@ 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...") +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)