Skip to content

Commit e614f56

Browse files
authored
Merge pull request #93 from brentru/enhance-send-data
Enhanced send_data method
2 parents ab83808 + bbb00f8 commit e614f56

File tree

4 files changed

+97
-84
lines changed

4 files changed

+97
-84
lines changed

Adafruit_IO/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "2.2"
1+
__version__ = "2.3"

Adafruit_IO/client.py

+72-67
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2020
# SOFTWARE.
2121
import json
22-
import pkg_resources
2322
import platform
23+
import pkg_resources
2424
# import logging
2525

2626
import requests
@@ -52,21 +52,50 @@ def __init__(self, username, key, proxies=None, base_url='https://io.adafruit.co
5252
self.username = username
5353
self.key = key
5454
self.proxies = proxies
55-
# self.logger = logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
55+
# self.logger = logging.basicConfig(level=logging.DEBUG,
56+
# format='%(asctime)s - %(levelname)s - %(message)s')
5657

5758
# Save URL without trailing slash as it will be added later when
5859
# constructing the path.
5960
self.base_url = base_url.rstrip('/')
6061

62+
@staticmethod
63+
def to_red(data):
64+
"""Hex color feed to red channel.
65+
:param int data: Color value, in hexadecimal.
66+
"""
67+
return ((int(data[1], 16))*16) + int(data[2], 16)
6168

62-
def _compose_url(self, path, is_time=None):
63-
if is_time: # return a call to https://io.adafruit.com/api/v2/time/{unit}
64-
return '{0}/api/{1}/{2}'.format(self.base_url, 'v2', path)
65-
else:
66-
return '{0}/api/{1}/{2}/{3}'.format(self.base_url, 'v2', self.username, path)
69+
@staticmethod
70+
def to_green(data):
71+
"""Hex color feed to green channel.
72+
:param int data: Color value, in hexadecimal.
73+
"""
74+
return (int(data[3], 16) * 16) + int(data[4], 16)
6775

76+
@staticmethod
77+
def to_blue(data):
78+
"""Hex color feed to blue channel.
79+
:param int data: Color value, in hexadecimal.
80+
"""
81+
return (int(data[5], 16) * 16) + int(data[6], 16)
82+
83+
@staticmethod
84+
def _headers(given):
85+
headers = default_headers.copy()
86+
headers.update(given)
87+
return headers
88+
89+
@staticmethod
90+
def _create_payload(value, metadata):
91+
if metadata is not None:
92+
payload = Data(value=value, lat=metadata['lat'], lon=metadata['lon'],
93+
ele=metadata['ele'], created_at=metadata['created_at'])
94+
return payload
95+
return Data(value=value)
6896

69-
def _handle_error(self, response):
97+
@staticmethod
98+
def _handle_error(response):
7099
# Throttling Error
71100
if response.status_code == 429:
72101
raise ThrottlingError()
@@ -78,10 +107,10 @@ def _handle_error(self, response):
78107
raise RequestError(response)
79108
# Else do nothing if there was no error.
80109

81-
def _headers(self, given):
82-
headers = default_headers.copy()
83-
headers.update(given)
84-
return headers
110+
def _compose_url(self, path, is_time=None):
111+
if is_time: # return a call to https://io.adafruit.com/api/v2/time/{unit}
112+
return '{0}/api/{1}/{2}'.format(self.base_url, 'v2', path)
113+
return '{0}/api/{1}/{2}/{3}'.format(self.base_url, 'v2', self.username, path)
85114

86115
def _get(self, path, is_time=None):
87116
response = requests.get(self._compose_url(path, is_time),
@@ -90,8 +119,7 @@ def _get(self, path, is_time=None):
90119
self._handle_error(response)
91120
if not is_time:
92121
return response.json()
93-
else: # time doesn't need to serialize into json, just return text
94-
return response.text
122+
return response.text
95123

96124
def _post(self, path, data):
97125
response = requests.post(self._compose_url(path),
@@ -105,20 +133,28 @@ def _post(self, path, data):
105133
def _delete(self, path):
106134
response = requests.delete(self._compose_url(path),
107135
headers=self._headers({'X-AIO-Key': self.key,
108-
'Content-Type': 'application/json'}),
136+
'Content-Type': 'application/json'}),
109137
proxies=self.proxies)
110138
self._handle_error(response)
111139

112140
# Data functionality.
113-
def send_data(self, feed, value):
141+
def send_data(self, feed, value, metadata=None, precision=None):
114142
"""Helper function to simplify adding a value to a feed. Will append the
115143
specified value to the feed identified by either name, key, or ID.
116144
Returns a Data instance with details about the newly appended row of data.
117145
Note that send_data now operates the same as append.
118146
:param string feed: Name/Key/ID of Adafruit IO feed.
119147
:param string value: Value to send.
148+
:param dict metadata: Optional metadata associated with the value.
149+
:param int precision: Optional amount of precision points to send.
120150
"""
121-
return self.create_data(feed, Data(value=value))
151+
if precision:
152+
try:
153+
value = round(value, precision)
154+
except NotImplementedError:
155+
raise NotImplementedError("Using the precision kwarg requires a float value")
156+
payload = self._create_payload(value, metadata)
157+
return self.create_data(feed, payload)
122158

123159
send = send_data
124160

@@ -144,42 +180,32 @@ def append(self, feed, value):
144180
"""
145181
return self.create_data(feed, Data(value=value))
146182

147-
def send_location_data(self, feed, lat, lon, ele, value=None):
148-
"""Sends locational data to a feed.
149-
:param string feed: Name/Key/ID of Adafruit IO feed.
150-
:param int lat: Latitude.
151-
:param int lon: Longitude.
152-
:param int ele: Elevation.
153-
:param int value: Optional value to send, defaults to None.
154-
"""
155-
return self.create_data(feed, Data(value=value,lat=lat, lon=lon, ele=ele))
156-
157183
def receive_time(self, time):
158184
"""Returns the time from the Adafruit IO server.
159185
:param string time: Time to be returned: `millis`, `seconds`, `ISO-8601`.
160186
"""
161187
timepath = "time/{0}".format(time)
162188
return self._get(timepath, is_time=True)
163-
189+
164190
def receive_weather(self, weather_id=None):
165191
"""Adafruit IO Weather Service, Powered by Dark Sky
166192
:param int id: optional ID for retrieving a specified weather record.
167193
"""
168194
if weather_id:
169-
weather_path = "integrations/weather/{0}".format(weather_id)
195+
weather_path = "integrations/weather/{0}".format(weather_id)
170196
else:
171-
weather_path = "integrations/weather"
197+
weather_path = "integrations/weather"
172198
return self._get(weather_path)
173-
174-
def receive_random(self, id=None):
199+
200+
def receive_random(self, randomizer_id=None):
175201
"""Access to Adafruit IO's Random Data
176202
service.
177-
:param int id: optional ID for retrieving a specified randomizer.
203+
:param int randomizer_id: optional ID for retrieving a specified randomizer.
178204
"""
179-
if id:
180-
random_path = "integrations/words/{0}".format(id)
205+
if randomizer_id:
206+
random_path = "integrations/words/{0}".format(randomizer_id)
181207
else:
182-
random_path = "integrations/words"
208+
random_path = "integrations/words"
183209
return self._get(random_path)
184210

185211
def receive(self, feed):
@@ -191,7 +217,7 @@ def receive(self, feed):
191217
return Data.from_dict(self._get(path))
192218

193219
def receive_next(self, feed):
194-
"""Retrieve the next unread value from the specified feed. Returns a Data
220+
"""Retrieve the next unread value from the specified feed. Returns a Data
195221
instance whose value property holds the retrieved value.
196222
:param string feed: Name/Key/ID of Adafruit IO feed.
197223
"""
@@ -215,16 +241,15 @@ def data(self, feed, data_id=None):
215241
if data_id is None:
216242
path = "feeds/{0}/data".format(feed)
217243
return list(map(Data.from_dict, self._get(path)))
218-
else:
219-
path = "feeds/{0}/data/{1}".format(feed, data_id)
220-
return Data.from_dict(self._get(path))
244+
path = "feeds/{0}/data/{1}".format(feed, data_id)
245+
return Data.from_dict(self._get(path))
221246

222247
def create_data(self, feed, data):
223248
"""Create a new row of data in the specified feed.
224-
Returns a Data instance with details about the newly
249+
Returns a Data instance with details about the newly
225250
appended row of data.
226251
:param string feed: Name/Key/ID of Adafruit IO feed.
227-
:param Data data: Instance of the Data class. Must have a value property set.
252+
:param Data data: Instance of the Data class. Must have a value property set.
228253
"""
229254
path = "feeds/{0}/data".format(feed)
230255
return Data.from_dict(self._post(path, data._asdict()))
@@ -237,24 +262,6 @@ def delete(self, feed, data_id):
237262
path = "feeds/{0}/data/{1}".format(feed, data_id)
238263
self._delete(path)
239264

240-
def toRed(self, data):
241-
"""Hex color feed to red channel.
242-
:param int data: Color value, in hexadecimal.
243-
"""
244-
return ((int(data[1], 16))*16) + int(data[2], 16)
245-
246-
def toGreen(self, data):
247-
"""Hex color feed to green channel.
248-
:param int data: Color value, in hexadecimal.
249-
"""
250-
return (int(data[3], 16) * 16) + int(data[4], 16)
251-
252-
def toBlue(self, data):
253-
"""Hex color feed to blue channel.
254-
:param int data: Color value, in hexadecimal.
255-
"""
256-
return (int(data[5], 16) * 16) + int(data[6], 16)
257-
258265
# feed functionality.
259266
def feeds(self, feed=None):
260267
"""Retrieve a list of all feeds, or the specified feed. If feed is not
@@ -264,9 +271,8 @@ def feeds(self, feed=None):
264271
if feed is None:
265272
path = "feeds"
266273
return list(map(Feed.from_dict, self._get(path)))
267-
else:
268-
path = "feeds/{0}".format(feed)
269-
return Feed.from_dict(self._get(path))
274+
path = "feeds/{0}".format(feed)
275+
return Feed.from_dict(self._get(path))
270276

271277
def create_feed(self, feed):
272278
"""Create the specified feed.
@@ -290,9 +296,8 @@ def groups(self, group=None):
290296
if group is None:
291297
path = "groups/"
292298
return list(map(Group.from_dict, self._get(path)))
293-
else:
294-
path = "groups/{0}".format(group)
295-
return Group.from_dict(self._get(path))
299+
path = "groups/{0}".format(group)
300+
return Group.from_dict(self._get(path))
296301

297302
def create_group(self, group):
298303
"""Create the specified group.
@@ -306,4 +311,4 @@ def delete_group(self, group):
306311
:param string group: Name/Key/ID of Adafruit IO Group.
307312
"""
308313
path = "groups/{0}".format(group)
309-
self._delete(path)
314+
self._delete(path)

examples/api/location.py

+15-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
"""
22
'location.py'
33
==================================
4-
Example of sending location over an
5-
Adafruit IO feed to a Map Dashboard
6-
block
4+
Example of sending metadata
5+
associated with a data point.
76
87
Author(s): Brent Rubell
98
"""
@@ -12,9 +11,14 @@
1211
from Adafruit_IO import Client, Feed, RequestError
1312

1413
# Set to your Adafruit IO key.
15-
ADAFRUIT_IO_USERNAME = 'YOUR_AIO_USERNAME'
14+
# Remember, your key is a secret,
15+
# so make sure not to publish it when you publish this code!
1616
ADAFRUIT_IO_KEY = 'YOUR_AIO_KEY'
1717

18+
# Set to your Adafruit IO username.
19+
# (go to https://accounts.adafruit.com to find your username)
20+
ADAFRUIT_IO_USERNAME = 'YOUR_AIO_USERNAME'
21+
1822
# Create an instance of the REST client.
1923
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
2024

@@ -25,12 +29,12 @@
2529
feed = Feed(name="location")
2630
location = aio.create_feed(feed)
2731

28-
29-
# Top Secret Adafruit HQ Location
30-
value = 1
31-
lat = 40.726190
32-
lon = -74.005334
33-
ele = 6 # elevation above sea level (meters)
32+
value = 42
33+
# Set metadata associated with value
34+
metadata = {'lat': 40.726190,
35+
'lon': -74.005334,
36+
'ele': -6,
37+
'created_at': None}
3438

3539
# Send location data to Adafruit IO
36-
aio.send_location_data(location.key, lat, lon, ele, value)
40+
aio.send_data(location.key, value, metadata)

tests/test_client.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,16 @@ def test_location_data(self):
133133
aio = self.get_client()
134134
self.ensure_feed_deleted(aio, 'testlocfeed')
135135
test_feed = aio.create_feed(Feed(name='testlocfeed'))
136-
aio.send_location_data(test_feed.key, 40, -74, 6, 0)
136+
metadata = {'lat': 40.726190,
137+
'lon': -74.005334,
138+
'ele': -6,
139+
'created_at': None}
140+
aio.send_data(test_feed.key, 40, metadata)
137141
data = aio.receive(test_feed.key)
138-
self.assertEqual(int(data.value), 0.0)
139-
self.assertEqual(float(data.lat), 40.0)
140-
self.assertEqual(float(data.lon), -74.0)
141-
self.assertEqual(float(data.ele), 6.0)
142+
self.assertEqual(int(data.value), 40)
143+
self.assertEqual(float(data.lat), 40.726190)
144+
self.assertEqual(float(data.lon), -74.005334)
145+
self.assertEqual(float(data.ele), -6.0)
142146

143147
# Test Feed Functionality
144148
def test_append_by_feed_name(self):

0 commit comments

Comments
 (0)