Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ setuptools-*
dist
build
eggs
.eggs
parts
bin
var
Expand Down
2 changes: 1 addition & 1 deletion Adafruit_IO/_version.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because some function arguments have been renamed for clarity, technically a breaking change.

Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.8.2"
__version__ = "3.0.0"
16 changes: 13 additions & 3 deletions Adafruit_IO/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@
from time import struct_time
import json
import platform
import pkg_resources
try:
from importlib.metadata import version as package_version # Python 3.8+
except ImportError:
try:
from importlib_metadata import version as package_version # Backport for <3.8
except ImportError: # pragma: no cover - fallback for older Python
package_version = None
import pkg_resources
import re
from urllib.parse import urlparse
from urllib.parse import parse_qs
Expand All @@ -35,8 +42,11 @@

DEFAULT_PAGE_LIMIT = 100

# set outgoing version, pulled from setup.py
version = pkg_resources.require("Adafruit_IO")[0].version
# set outgoing version, pulled from package metadata
if package_version is not None:
version = package_version("Adafruit_IO")
else:
version = pkg_resources.require("Adafruit_IO")[0].version
default_headers = {
'User-Agent': 'AdafruitIO-Python/{0} ({1}, {2} {3})'.format(version,
platform.platform(),
Expand Down
57 changes: 40 additions & 17 deletions Adafruit_IO/mqtt_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import logging
import re

import paho.mqtt.client as mqtt
import sys
Expand All @@ -35,6 +36,25 @@
"forecast_hours_24", "forecast_days_1",
"forecast_days_2", "forecast_days_5",]


def validate_feed_key(feed_key):
"""Validates a provided feed key against Adafruit IO's system rules.
https://learn.adafruit.com/naming-things-in-adafruit-io/the-two-feed-identifiers

:param str feed_key: The feed key to validate.
:raises ValueError: If the feed key is too long.
:raises TypeError: If the feed key contains invalid characters or is empty.
"""
if len(feed_key) > 128: # validate feed key length
raise ValueError("Feed key must be less than 128 characters.")
if not bool(
re.match(r"^[a-zA-Z0-9-]+((\/|\.)[a-zA-Z0-9-]+)?$", feed_key)
): # validate key naming scheme
raise TypeError(
"Feed key must contain English letters, numbers, dash, and a period or a forward slash."
)


class MQTTClient(object):
"""Interface for publishing and subscribing to feed changes on Adafruit IO
using the MQTT protocol.
Expand Down Expand Up @@ -198,21 +218,22 @@ def loop(self, timeout_sec=1.0):
"""
self._client.loop(timeout=timeout_sec)

def subscribe(self, feed_id, feed_user=None, qos=0):
def subscribe(self, feed_key, feed_user=None, qos=0):
"""Subscribe to changes on the specified feed. When the feed is updated
the on_message function will be called with the feed_id and new value.
the on_message function will be called with the feed_key and new value.

:param str feed_id: The key of the feed to subscribe to.
:param str feed_key: The key of the feed to subscribe to.
:param str feed_user: Optional, identifies feed owner. Used for feed sharing.
:param int qos: The QoS to use when subscribing. Defaults to 0.

"""
validate_feed_key(feed_key)
if qos > 1:
raise MQTTError("Adafruit IO only supports a QoS level of 0 or 1.")
if feed_user is not None:
(res, mid) = self._client.subscribe('{0}/feeds/{1}'.format(feed_user, feed_id, qos=qos))
(res, mid) = self._client.subscribe('{0}/feeds/{1}'.format(feed_user, feed_key), qos=qos)
else:
(res, mid) = self._client.subscribe('{0}/feeds/{1}'.format(self._username, feed_id), qos=qos)
(res, mid) = self._client.subscribe('{0}/feeds/{1}'.format(self._username, feed_key), qos=qos)
return res, mid

def subscribe_group(self, group_id, qos=0):
Expand Down Expand Up @@ -264,43 +285,45 @@ def subscribe_time(self, time):
raise TypeError('Invalid Time Feed Specified.')
return

def unsubscribe(self, feed_id=None, group_id=None):
def unsubscribe(self, feed_key=None, group_id=None):
"""Unsubscribes from a specified MQTT topic.
Note: this does not prevent publishing to a topic, it will unsubscribe
from receiving messages via on_message.
"""
if feed_id is not None:
self._client.unsubscribe('{0}/feeds/{1}'.format(self._username, feed_id))
if feed_key is not None:
validate_feed_key(feed_key)
self._client.unsubscribe('{0}/feeds/{1}'.format(self._username, feed_key))
elif group_id is not None:
self._client.unsubscribe('{0}/groups/{1}'.format(self._username, group_id))
else:
raise TypeError('Invalid topic type specified.')
return

def receive(self, feed_id):
def receive(self, feed_key):
"""Receive the last published value from a specified feed.

:param string feed_id: The ID of the feed to update.
:parm string value: The new value to publish to the feed
:param string feed_key: The key of the feed to retrieve the value from.
"""
(res, self._pub_mid) = self._client.publish('{0}/feeds/{1}/get'.format(self._username, feed_id),
validate_feed_key(feed_key)
(res, self._pub_mid) = self._client.publish('{0}/feeds/{1}/get'.format(self._username, feed_key),
payload='')

def publish(self, feed_id, value=None, group_id=None, feed_user=None):
def publish(self, feed_key, value=None, group_id=None, feed_user=None):
"""Publish a value to a specified feed.

Params:
- feed_id: The id of the feed to update.
- feed_key: The key of the feed to update.
- value: The new value to publish to the feed.
- (optional) group_id: The id of the group to update.
- (optional) feed_user: The feed owner's username. Used for Sharing Feeds.
"""
validate_feed_key(feed_key)
if feed_user is not None: # shared feed
(res, self._pub_mid) = self._client.publish('{0}/feeds/{1}'.format(feed_user, feed_id),
(res, self._pub_mid) = self._client.publish('{0}/feeds/{1}'.format(feed_user, feed_key),
payload=value)
elif group_id is not None: # group-specified feed
self._client.publish('{0}/feeds/{1}.{2}'.format(self._username, group_id, feed_id),
self._client.publish('{0}/feeds/{1}.{2}'.format(self._username, group_id, feed_key),
payload=value)
else: # regular feed
(res, self._pub_mid) = self._client.publish('{0}/feeds/{1}'.format(self._username, feed_id),
(res, self._pub_mid) = self._client.publish('{0}/feeds/{1}'.format(self._username, feed_key),
payload=value)
17 changes: 10 additions & 7 deletions examples/api/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@
# API client.
# Author: Tony Dicola, Justin Cooper

# Import Adafruit IO REST client.
from Adafruit_IO import Client, Feed, Data, RequestError
# Import standard python modules.
import datetime
import os

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure not to publish it when you publish this code!
ADAFRUIT_IO_KEY = 'YOUR_AIO_KEY'
# Import Adafruit IO REST client.
from Adafruit_IO import Client, Feed, Data, RequestError

# Set to your Adafruit IO username.
# (go to https://accounts.adafruit.com to find your username)
ADAFRUIT_IO_USERNAME = 'YOUR_AIO_USERNAME'
ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_USERNAME', 'YOUR_AIO_USERNAME')

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure **not** to publish it when you publish this code!
ADAFRUIT_IO_KEY = os.getenv('ADAFRUIT_IO_KEY', 'YOUR_AIO_KEY')

# Create an instance of the REST client.
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
Expand Down
16 changes: 9 additions & 7 deletions examples/api/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@
# API client.
# Author: Tony Dicola, Justin Cooper, Brent Rubell

# Import standard python modules.
import os

# Import Adafruit IO REST client.
from Adafruit_IO import Client, Feed
import json

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure not to publish it when you publish this code!
ADAFRUIT_IO_KEY = 'YOUR_AIO_KEY'

# Set to your Adafruit IO username.
# (go to https://accounts.adafruit.com to find your username)
ADAFRUIT_IO_USERNAME = 'YOUR_AIO_USERNAME'
ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_USERNAME', 'YOUR_AIO_USERNAME')

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure **not** to publish it when you publish this code!
ADAFRUIT_IO_KEY = os.getenv('ADAFRUIT_IO_KEY', 'YOUR_AIO_KEY')

# Create an instance of the REST client.
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
Expand Down
15 changes: 9 additions & 6 deletions examples/api/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@
Author(s): Brent Rubell
"""

# Import standard python modules.
import os

# Import Adafruit IO REST client.
from Adafruit_IO import Client, Feed, RequestError

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure not to publish it when you publish this code!
ADAFRUIT_IO_KEY = 'YOUR_AIO_KEY'

# Set to your Adafruit IO username.
# (go to https://accounts.adafruit.com to find your username)
ADAFRUIT_IO_USERNAME = 'YOUR_AIO_USERNAME'
ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_USERNAME', 'YOUR_AIO_USERNAME')

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure **not** to publish it when you publish this code!
ADAFRUIT_IO_KEY = os.getenv('ADAFRUIT_IO_KEY', 'YOUR_AIO_KEY')

# Create an instance of the REST client.
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
Expand Down
14 changes: 10 additions & 4 deletions examples/api/random_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@

Author(s): Brent Rubell for Adafruit Industries
"""
# Import JSON for forecast parsing
# Import standard python modules
import json
import os
# Import Adafruit IO REST client.
from Adafruit_IO import Client, Feed, RequestError
from Adafruit_IO import Client

# Set to your Adafruit IO username.
# (go to https://accounts.adafruit.com to find your username)
ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_USERNAME', 'YOUR_IO_USERNAME')

# Set to your Adafruit IO key.
ADAFRUIT_IO_USERNAME = 'YOUR_IO_USERNAME'
ADAFRUIT_IO_KEY = 'YOUR_IO_KEY'
# Remember, your key is a secret,
# so make sure **not** to publish it when you publish this code!
ADAFRUIT_IO_KEY = os.getenv('ADAFRUIT_IO_KEY', 'YOUR_IO_KEY')

# Create an instance of the REST client.
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
Expand Down
15 changes: 9 additions & 6 deletions examples/api/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
# API client.
# Author: Tony DiCola

# Import standard python modules.
import os

# Import Adafruit IO REST client.
from Adafruit_IO import Client, RequestError, Feed

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure not to publish it when you publish this code!
ADAFRUIT_IO_KEY = 'YOUR_AIO_KEY'

# Set to your Adafruit IO username.
# (go to https://accounts.adafruit.com to find your username)
ADAFRUIT_IO_USERNAME = 'YOUR_AIO_USERNAME'
ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_USERNAME', 'YOUR_AIO_USERNAME')

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure **not** to publish it when you publish this code!
ADAFRUIT_IO_KEY = os.getenv('ADAFRUIT_IO_KEY', 'YOUR_AIO_KEY')

# Create an instance of the REST client.
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
Expand Down
48 changes: 33 additions & 15 deletions examples/api/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,22 @@

Author(s): Brent Rubell for Adafruit Industries
"""
# Import JSON for forecast parsing

# Import JSON for forecast parsing, os for environment variables.
import json
import os

# Import Adafruit IO REST client.
from Adafruit_IO import Client, Feed, RequestError
from Adafruit_IO import Client

# Set to your Adafruit IO username.
# (go to https://accounts.adafruit.com to find your username)
ADAFRUIT_IO_USERNAME = os.getenv("ADAFRUIT_IO_USERNAME", "YOUR_IO_USERNAME")

# Set to your Adafruit IO key.
ADAFRUIT_IO_USERNAME = 'YOUR_IO_USERNAME'
ADAFRUIT_IO_KEY = 'YOUR_IO_PASSWORD'
# Remember, your key is a secret,
# so make sure **not** to publish it when you publish this code!
ADAFRUIT_IO_KEY = os.getenv("ADAFRUIT_IO_KEY", "YOUR_IO_PASSWORD")

# Create an instance of the REST client.
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
Expand All @@ -24,18 +32,28 @@
forecast = json.loads(weather)

# Parse the current forecast
current = forecast['current']
print('Current Forecast')
print('It is {0} and {1}.'.format(current['summary'], current['temperature']))
current = forecast["current"]
print("Current Forecast")
print("It is {0} and {1}.".format(current["summary"], current["temperature"]))

# Parse the two day forecast
forecast_days_2 = forecast['forecast_days_2']
print('\nWeather in Two Days')
print('It will be {0} with a high of {1}F and a low of {2}F.'.format(
forecast_days_2['summary'], forecast_days_2['temperatureLow'], forecast_days_2['temperatureHigh']))
forecast_days_2 = forecast["forecast_days_2"]
print("\nWeather in Two Days")
print(
"It will be {0} with a high of {1}F and a low of {2}F.".format(
forecast_days_2["summary"],
forecast_days_2["temperatureLow"],
forecast_days_2["temperatureHigh"],
)
)

# Parse the five day forecast
forecast_days_5 = forecast['forecast_days_5']
print('\nWeather in Five Days')
print('It will be {0} with a high of {1}F and a low of {2}F.'.format(
forecast_days_5['summary'], forecast_days_5['temperatureLow'], forecast_days_5['temperatureHigh']))
forecast_days_5 = forecast["forecast_days_5"]
print("\nWeather in Five Days")
print(
"It will be {0} with a high of {1}F and a low of {2}F.".format(
forecast_days_5["summary"],
forecast_days_5["temperatureLow"],
forecast_days_5["temperatureHigh"],
)
)
13 changes: 7 additions & 6 deletions examples/basics/analog_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
(https://github.com/adafruit/Adafruit_CircuitPython_MCP3xxx)
"""
# Import standard python modules
import os
import time

# import Adafruit Blinka
Expand All @@ -27,14 +28,14 @@
from adafruit_mcp3xxx.mcp3008 import MCP3008
from adafruit_mcp3xxx.analog_in import AnalogIn

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure not to publish it when you publish this code!
ADAFRUIT_IO_KEY = 'YOUR_AIO_KEY'

# Set to your Adafruit IO username.
# (go to https://accounts.adafruit.com to find your username)
ADAFRUIT_IO_USERNAME = 'YOUR_AIO_USERNAME'
ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_USERNAME', 'YOUR_AIO_USERNAME')

# Set to your Adafruit IO key.
# Remember, your key is a secret,
# so make sure **not** to publish it when you publish this code!
ADAFRUIT_IO_KEY = os.getenv('ADAFRUIT_IO_KEY', 'YOUR_AIO_KEY')
# Create an instance of the REST client
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)

Expand Down
Loading