Skip to content

Commit 66ea1f4

Browse files
authored
Merge branch 'api-v2' into v2-examples
2 parents ed858ae + 721bdc3 commit 66ea1f4

27 files changed

+927
-506
lines changed

Diff for: .travis.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# .travis.yml for Adafruit IO Python Client Library
2+
3+
dist: trusty
4+
sudo: false
5+
language: python
6+
python:
7+
- "3.6"
8+
9+
cache:
10+
pip: true
11+
12+
install:
13+
- pip install pylint Sphinx sphinx-rtd-theme
14+
15+
script:
16+
# test adafruit_io_basics examples
17+
- pylint --disable=missing-docstring,invalid-name examples/aio_basics/*.py
18+
# test API examples
19+
pylint --disable=missing-docstring,invalid-name examples/api/*.py
20+
# test MQTT examples
21+
pylint --disable=missing-docstring,invalid-name examples/aio_basics/*.py
22+
# finally, let's build the docs
23+
- cd docs && sphinx-build -E -W -b html . _build/html

Diff for: Adafruit_IO/client.py

+32-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2014 Adafruit Industries
1+
# Copyright (c) 2018 Adafruit Industries
22
# Authors: Justin Cooper & Tony DiCola
33

44
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -60,8 +60,11 @@ def __init__(self, username, key, proxies=None, base_url='https://io.adafruit.co
6060
# constructing the path.
6161
self.base_url = base_url.rstrip('/')
6262

63-
def _compose_url(self, path):
64-
return '{0}/api/{1}/{2}/{3}'.format(self.base_url, self.api_version, self.username, path)
63+
def _compose_url(self, path, is_time=None):
64+
if not is_time:
65+
return '{0}/api/{1}/{2}/{3}'.format(self.base_url, self.api_version, self.username, path)
66+
else: # return a call to https://io.adafruit.com/api/v2/time/{unit}
67+
return '{0}/api/{1}/{2}'.format(self.base_url, self.api_version, path)
6568

6669

6770
def _handle_error(self, response):
@@ -78,12 +81,15 @@ def _headers(self, given):
7881
headers.update(given)
7982
return headers
8083

81-
def _get(self, path):
82-
response = requests.get(self._compose_url(path),
84+
def _get(self, path, is_time=None):
85+
response = requests.get(self._compose_url(path, is_time),
8386
headers=self._headers({'X-AIO-Key': self.key}),
8487
proxies=self.proxies)
8588
self._handle_error(response)
86-
return response.json()
89+
if not is_time:
90+
return response.json()
91+
else: # time doesn't need to serialize into json, just return text
92+
return response.text
8793

8894
def _post(self, path, data):
8995
response = requests.post(self._compose_url(path),
@@ -130,6 +136,26 @@ def append(self, feed, value):
130136
"""
131137
return self.create_data(feed, Data(value=value))
132138

139+
def send_location_data(self, feed, value, lat, lon, ele):
140+
"""Sends locational data to a feed
141+
142+
args:
143+
- lat: latitude
144+
- lon: logitude
145+
- ele: elevation
146+
- (optional) value: value to send to the feed
147+
"""
148+
return self.create_data(feed, Data(value = value,lat=lat, lon=lon, ele=ele))
149+
150+
def receive_time(self, time):
151+
"""Returns the time from the Adafruit IO server.
152+
153+
args:
154+
- time (string): millis, seconds, ISO-8601
155+
"""
156+
timepath = "time/{0}".format(time)
157+
return self._get(timepath, is_time=True)
158+
133159
def receive(self, feed):
134160
"""Retrieve the most recent value for the specified feed. Feed can be a
135161
feed ID, feed key, or feed name. Returns a Data instance whose value

Diff for: Adafruit_IO/errors.py

+16
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121

2222
import json
2323

24+
# MQTT RC Error Types
25+
MQTT_ERRORS = [ 'Connection successful',
26+
'Incorrect protocol version',
27+
'Invalid Client ID',
28+
'Server unavailable ',
29+
'Bad username or password',
30+
'Not authorized' ]
31+
2432
class AdafruitIOError(Exception):
2533
"""Base class for all Adafruit IO request failures."""
2634
pass
@@ -49,3 +57,11 @@ def __init__(self):
4957
super(ThrottlingError, self).__init__("Exceeded the limit of Adafruit IO " \
5058
"requests in a short period of time. Please reduce the rate of requests " \
5159
"and try again later.")
60+
61+
class MQTTError(Exception):
62+
"""Handles connection attempt failed errors.
63+
"""
64+
def __init__(self, response):
65+
error = MQTT_ERRORS[response]
66+
super(MQTTError, self).__init__(error)
67+
pass

Diff for: Adafruit_IO/model.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@
3636
'feed_id',
3737
'expiration',
3838
'position',
39-
'id' ]
39+
'id',
40+
'lat',
41+
'lon',
42+
'ele']
4043

4144
FEED_FIELDS = [ 'name',
4245
'key',

Diff for: Adafruit_IO/mqtt_client.py

+40-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
import logging
2222

2323
import paho.mqtt.client as mqtt
24-
24+
import sys
25+
from .errors import MQTTError, RequestError
2526

2627
# How long to wait before sending a keep alive (paho-mqtt configuration).
2728
KEEP_ALIVE_SEC = 60 # One minute
@@ -34,23 +35,29 @@ class MQTTClient(object):
3435
using the MQTT protocol.
3536
"""
3637

37-
def __init__(self, username, key, service_host='io.adafruit.com', service_port=1883):
38+
def __init__(self, username, key, service_host='io.adafruit.com', secure=True):
3839
"""Create instance of MQTT client.
3940
40-
Required parameters:
41-
- username: The Adafruit.IO username for your account (found on the
42-
accounts site https://accounts.adafruit.com/).
43-
- key: The Adafruit.IO access key for your account.
41+
:param username: Adafruit.IO Username for your account.
42+
:param key: Adafruit IO access key (AIO Key) for your account.
43+
:param secure: (optional, boolean) Switches secure/insecure connections
4444
"""
4545
self._username = username
4646
self._service_host = service_host
47-
self._service_port = service_port
47+
if secure:
48+
self._service_port = 8883
49+
elif not secure:
50+
self._service_port = 1883
4851
# Initialize event callbacks to be None so they don't fire.
4952
self.on_connect = None
5053
self.on_disconnect = None
5154
self.on_message = None
5255
# Initialize MQTT client.
5356
self._client = mqtt.Client()
57+
if secure:
58+
self._client.tls_set_context()
59+
elif not secure:
60+
print('**THIS CONNECTION IS INSECURE** SSL/TLS not supported for this platform')
5461
self._client.username_pw_set(username, key)
5562
self._client.on_connect = self._mqtt_connect
5663
self._client.on_disconnect = self._mqtt_disconnect
@@ -62,11 +69,12 @@ def _mqtt_connect(self, client, userdata, flags, rc):
6269
# Check if the result code is success (0) or some error (non-zero) and
6370
# raise an exception if failed.
6471
if rc == 0:
72+
#raise RequestError(rc)
6573
self._connected = True
74+
print('Connected to Adafruit IO!')
6675
else:
67-
# TODO: Make explicit exception classes for these failures:
68-
# 0: Connection successful 1: Connection refused - incorrect protocol version 2: Connection refused - invalid client identifier 3: Connection refused - server unavailable 4: Connection refused - bad username or password 5: Connection refused - not authorised 6-255: Currently unused.
69-
raise RuntimeError('Error connecting to Adafruit IO with rc: {0}'.format(rc))
76+
# handle RC errors within `errors.py`'s MQTTError class
77+
raise MQTTError(rc)
7078
# Call the on_connect callback if available.
7179
if self.on_connect is not None:
7280
self.on_connect(self)
@@ -78,7 +86,8 @@ def _mqtt_disconnect(self, client, userdata, rc):
7886
# log the RC as an error. Continue on to call any disconnect handler
7987
# so clients can potentially recover gracefully.
8088
if rc != 0:
81-
logger.debug('Unexpected disconnect with rc: {0}'.format(rc))
89+
raise MQTTError(rc)
90+
print('Disconnected from Adafruit IO!')
8291
# Call the on_disconnect callback if available.
8392
if self.on_disconnect is not None:
8493
self.on_disconnect(self)
@@ -91,7 +100,10 @@ def _mqtt_message(self, client, userdata, msg):
91100
if self.on_message is not None and self._username == parsed_topic[0]:
92101
feed = parsed_topic[2]
93102
payload = '' if msg.payload is None else msg.payload.decode('utf-8')
94-
self.on_message(self, feed, payload)
103+
elif self.on_message is not None and parsed_topic[0] == 'time':
104+
feed = parsed_topic[0]
105+
payload = msg.payload.decode('utf-8')
106+
self.on_message(self, feed, payload)
95107

96108
def connect(self, **kwargs):
97109
"""Connect to the Adafruit.IO service. Must be called before any loop
@@ -154,6 +166,22 @@ def subscribe(self, feed_id):
154166
"""
155167
self._client.subscribe('{0}/feeds/{1}'.format(self._username, feed_id))
156168

169+
def subscribe_time(self, time):
170+
"""Subscribe to changes on the Adafruit IO time feeds. When the feed is
171+
updated, the on_message function will be called and publish a new value:
172+
time =
173+
millis: milliseconds
174+
seconds: seconds
175+
iso: ISO-8601 (https://en.wikipedia.org/wiki/ISO_8601)
176+
"""
177+
if time == 'millis' or time == 'seconds':
178+
self._client.subscribe('time/{0}'.format(time))
179+
elif time == 'iso':
180+
self._client.subscribe('time/ISO-8601')
181+
else:
182+
print('ERROR: Invalid time type specified')
183+
return
184+
157185
def publish(self, feed_id, value):
158186
"""Publish a value to a specified feed.
159187

Diff for: CODE_OF_CONDUCT.md

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Contributor Covenant Code of Conduct
2+
3+
## Our Pledge
4+
5+
In the interest of fostering an open and welcoming environment, we as
6+
contributors and maintainers pledge to making participation in our project and
7+
our community a harassment-free experience for everyone, regardless of age, body
8+
size, disability, ethnicity, gender identity and expression, level of experience,
9+
nationality, personal appearance, race, religion, or sexual identity and
10+
orientation.
11+
12+
## Our Standards
13+
14+
Examples of behavior that contributes to creating a positive environment
15+
include:
16+
17+
* Using welcoming and inclusive language
18+
* Being respectful of differing viewpoints and experiences
19+
* Gracefully accepting constructive criticism
20+
* Focusing on what is best for the community
21+
* Showing empathy towards other community members
22+
23+
Examples of unacceptable behavior by participants include:
24+
25+
* The use of sexualized language or imagery and unwelcome sexual attention or
26+
advances
27+
* Trolling, insulting/derogatory comments, and personal or political attacks
28+
* Public or private harassment
29+
* Publishing others' private information, such as a physical or electronic
30+
address, without explicit permission
31+
* Other conduct which could reasonably be considered inappropriate in a
32+
professional setting
33+
34+
## Our Responsibilities
35+
36+
Project maintainers are responsible for clarifying the standards of acceptable
37+
behavior and are expected to take appropriate and fair corrective action in
38+
response to any instances of unacceptable behavior.
39+
40+
Project maintainers have the right and responsibility to remove, edit, or
41+
reject comments, commits, code, wiki edits, issues, and other contributions
42+
that are not aligned to this Code of Conduct, or to ban temporarily or
43+
permanently any contributor for other behaviors that they deem inappropriate,
44+
threatening, offensive, or harmful.
45+
46+
## Scope
47+
48+
This Code of Conduct applies both within project spaces and in public spaces
49+
when an individual is representing the project or its community. Examples of
50+
representing a project or community include using an official project e-mail
51+
address, posting via an official social media account, or acting as an appointed
52+
representative at an online or offline event. Representation of a project may be
53+
further defined and clarified by project maintainers.
54+
55+
## Enforcement
56+
57+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
58+
reported by contacting the project team at support@adafruit.com. All
59+
complaints will be reviewed and investigated and will result in a response that
60+
is deemed necessary and appropriate to the circumstances. The project team is
61+
obligated to maintain confidentiality with regard to the reporter of an incident.
62+
Further details of specific enforcement policies may be posted separately.
63+
64+
Project maintainers who do not follow or enforce the Code of Conduct in good
65+
faith may face temporary or permanent repercussions as determined by other
66+
members of the project's leadership.
67+
68+
## Attribution
69+
70+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71+
available at [http://contributor-covenant.org/version/1/4][version]
72+
73+
[homepage]: http://contributor-covenant.org
74+
[version]: http://contributor-covenant.org/version/1/4/

Diff for: LICENSE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2014 Adafruit
1+
Copyright (c) 2014-2018 Adafruit
22
Author: Justin Cooper and Tony DiCola
33

44
MIT License

Diff for: MANIFEST.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
include README.md
1+
include README.rst

0 commit comments

Comments
 (0)