Compare commits

...

8 commits

Author SHA1 Message Date
tyeth
ab8aad0b68 format changes 2025-05-22 18:13:55 +01:00
tyeth
8e0c35906d Add send_group_multiple_data method 2025-05-22 18:09:23 +01:00
tyeth
c3d2a66fa7 Add client.send_group_multiple_data tests 2025-05-22 18:09:11 +01:00
tyeth
db45babec5 Correct key check to hasattr 2025-05-22 17:44:36 +01:00
tyeth
2a271ef31c Add GroupFeedData to exports 2025-05-22 17:43:48 +01:00
tyeth
f38b5bc751 Avoid import pkg_resources 2025-05-22 17:43:14 +01:00
Tyeth Gundry
c122320296 Add test for send group multiple feeds data (send_group_multiple_data) 2025-05-22 17:25:56 +01:00
Tyeth Gundry
f7f9d1ba5a Add model 2025-05-22 17:25:29 +01:00
5 changed files with 125 additions and 7 deletions

View file

@ -21,5 +21,5 @@
from .client import Client
from .mqtt_client import MQTTClient
from .errors import AdafruitIOError, RequestError, ThrottlingError, MQTTError
from .model import Data, Feed, Group, Dashboard, Block, Layout
from .model import Data, Feed, Group, Dashboard, Block, Layout, GroupFeedData
from ._version import __version__

View file

@ -22,7 +22,13 @@ import time
from time import struct_time
import json
import platform
import pkg_resources
try:
from importlib.metadata import version as pkg_version # Python 3.8+
except ImportError:
try:
from importlib_metadata import version as pkg_version # Backport for <3.8
except ImportError:
pkg_version = None
import re
from urllib.parse import urlparse
from urllib.parse import parse_qs
@ -30,13 +36,26 @@ from urllib.parse import parse_qs
import requests
from .errors import RequestError, ThrottlingError
from .model import Data, Feed, Group, Dashboard, Block, Layout
DEFAULT_PAGE_LIMIT = 100
# set outgoing version, pulled from setup.py
version = pkg_resources.require("Adafruit_IO")[0].version
# set outgoing version, pulled from setup.py or package metadata
_package_name = "Adafruit_IO"
_version = None
if pkg_version:
try:
_version = pkg_version(_package_name)
except Exception:
pass
if not _version:
try:
_version = pkg_resources.require(_package_name)[0].version
except Exception:
_version = "unknown"
version = _version
default_headers = {
'User-Agent': 'AdafruitIO-Python/{0} ({1}, {2} {3})'.format(version,
platform.platform(),
@ -185,6 +204,26 @@ class Client(object):
data_dict = type(data_list)((data._asdict() for data in data_list))
self._post(path, {"data": data_dict})
def send_group_multiple_data(self, group, data_list):
"""Create a new row of data in the specified group. Group can be a group
ID, group key, or group name. Data must be a list of GroupFeedData objects, or
a Dict with a feeds property, containing a list of GroupFeedData objects with
at least a value property and key set on it. Optionally, metadata (created_at,
lat/lon/ele) can be set at the root object level.
Returns a Data instance with details about the newly appended rows of data.
:param string group: Name/Key/ID of Adafruit IO group.
:param List[GroupFeedData]|Dict data_list: Multiple data values with keys.
"""
path = "groups/{0}/data".format(group)
if isinstance(data_list, list):
data_dict = {"feeds": [data._asdict() for data in data_list]}
elif isinstance(data_list, dict):
data_dict = data_list
else:
raise TypeError("data_list must be a dict or list")
self._post(path, data_dict)
def append(self, feed, value):
"""Helper function to simplify adding a value to a feed. Will append the
specified value to the feed identified by either name, key, or ID.

View file

@ -41,6 +41,10 @@ DATA_FIELDS = [ 'created_epoch',
'lon',
'ele']
# List of fields/properties for GroupFeedData object
GROUPFEEDDATA_FIELDS = [ 'value',
'key']
FEED_FIELDS = [ 'name',
'key',
'id',
@ -95,12 +99,14 @@ Group = namedtuple('Group', GROUP_FIELDS)
Dashboard = namedtuple('Dashboard', DASHBOARD_FIELDS)
Block = namedtuple('Block', BLOCK_FIELDS)
Layout = namedtuple('Layout', LAYOUT_FIELDS)
GroupFeedData = namedtuple('GroupFeedData', GROUPFEEDDATA_FIELDS)
# Magic incantation to make all parameters to the initializers optional with a
# default value of None.
Group.__new__.__defaults__ = tuple(None for x in GROUP_FIELDS)
Data.__new__.__defaults__ = tuple(None for x in DATA_FIELDS)
Layout.__new__.__defaults__ = tuple(None for x in LAYOUT_FIELDS)
GroupFeedData.__new__.__defaults__ = tuple(None for x in GROUPFEEDDATA_FIELDS)
# explicitly set dashboard values so that 'color_mode' is 'dark'
Dashboard.__new__.__defaults__ = (None, None, None, False, "dark", True, None, None)
@ -147,3 +153,4 @@ Group.from_dict = classmethod(_group_from_dict)
Dashboard.from_dict = classmethod(_dashboard_from_dict)
Block.from_dict = classmethod(_from_dict)
Layout.from_dict = classmethod(_from_dict)
GroupFeedData.from_dict = classmethod(_from_dict)

View file

@ -3,7 +3,7 @@
import time
import unittest
from Adafruit_IO import Client, Data, Feed, Group, Dashboard, Block, Layout, RequestError
from Adafruit_IO import Client, Data, Feed, Group, Dashboard, Block, Layout, RequestError, GroupFeedData
import base
@ -95,6 +95,61 @@ class TestClient(base.IOTestCase):
data = io.receive(test_feed.key)
self.assertEqual(int(data.value), 42)
def test_send_group_multiple_data_as_list(self):
"""send_group_multiple_data
"""
io = self.get_client()
self.ensure_group_deleted(io, 'testgroup')
self.ensure_feed_deleted(io, 'testfeed1')
self.ensure_feed_deleted(io, 'testfeed2')
test_group = io.create_group(Group(name="testgroup"))
test_feed1 = io.create_feed(Feed(name="testfeed1"), test_group.key)
test_feed2 = io.create_feed(Feed(name="testfeed2"), test_group.key)
data_list = [
GroupFeedData(value=42, key=test_feed1.key.replace(
test_group.key + ".", "")),
GroupFeedData(value=42, key=test_feed2.key.replace(
test_group.key + ".", ""))
]
io.send_group_multiple_data(test_group.key, data_list)
data = io.receive(test_feed1.key)
self.assertEqual(int(data.value), 42)
data = io.receive(test_feed2.key)
self.assertEqual(int(data.value), 42)
self.ensure_feed_deleted(io, 'testfeed1')
self.ensure_feed_deleted(io, 'testfeed2')
self.ensure_group_deleted(io, 'testgroup')
def test_send_group_multiple_data_as_dict(self):
"""send_group_multiple_data
"""
io = self.get_client()
self.ensure_group_deleted(io, 'testgroup')
self.ensure_feed_deleted(io, 'testfeed1')
self.ensure_feed_deleted(io, 'testfeed2')
test_group = io.create_group(Group(name="testgroup"))
test_feed1 = io.create_feed(Feed(name="testfeed1"), test_group.key)
test_feed2 = io.create_feed(Feed(name="testfeed2"), test_group.key)
data_dict = {
"feeds": [
{"key": test_feed1.key.replace(
test_group.key + ".", ""), "value": 43},
{"key": test_feed2.key.replace(
test_group.key + ".", ""), "value": 43}
],
"lat": 40.726190,
"lon": -74.005334,
"ele": -6,
}
io.send_group_multiple_data(test_group.key, data_dict)
data = io.receive(test_feed1.key)
self.assertEqual(int(data.value), 43)
data = io.receive(test_feed2.key)
self.assertEqual(int(data.value), 43)
self.ensure_feed_deleted(io, 'testfeed1')
self.ensure_feed_deleted(io, 'testfeed2')
self.ensure_group_deleted(io, 'testgroup')
def test_receive_next(self):
"""receive_next
"""

View file

@ -18,7 +18,7 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from Adafruit_IO import Data, Feed, Group, Dashboard, Block, Layout
from Adafruit_IO import Data, Feed, Group, Dashboard, Block, Layout, GroupFeedData
import base
@ -59,7 +59,7 @@ class TestData(base.IOTestCase):
self.assertIsNone(feed.license)
self.assertIsNone(feed.status_notify)
self.assertIsNone(feed.status_timeout)
def test_group_properties_are_optional(self):
group = Group(name="foo")
self.assertEqual(group.name, 'foo')
@ -116,3 +116,20 @@ class TestData(base.IOTestCase):
self.assertIsNone(data.expiration)
self.assertIsNone(data.position)
self.assertIsNone(data.id)
class TestGroupFeedData(base.IOTestCase):
def test_groupfeeddata_properties_are_optional(self):
"""GroupFeedData fields have optional properties
"""
data = GroupFeedData(value='foo', key='test_key')
self.assertEqual(data.value, 'foo')
self.assertEqual(data.key, 'test_key')
def test_groupfeeddata_from_dict_ignores_unknown_items(self):
data = GroupFeedData.from_dict(
{'value': 'foo', 'key': 'test_key', 'unknown_param': 42})
self.assertEqual(data.value, 'foo')
self.assertEqual(data.key, 'test_key')
self.assertFalse(hasattr(data, 'unknown_param'))