Feed sharing (#64)

Adding Feed Sharing into IO Python MQTT_Client
This commit is contained in:
brentrubell 2018-08-01 17:21:11 -04:00 committed by GitHub
parent 1765438bc1
commit d694464419
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 10 deletions

View file

@ -52,6 +52,7 @@ class MQTTClient(object):
self.on_connect = None self.on_connect = None
self.on_disconnect = None self.on_disconnect = None
self.on_message = None self.on_message = None
self.on_subscribe = None
# Initialize MQTT client. # Initialize MQTT client.
self._client = mqtt.Client() self._client = mqtt.Client()
if secure: if secure:
@ -66,6 +67,7 @@ class MQTTClient(object):
self._client.on_message = self._mqtt_message self._client.on_message = self._mqtt_message
self._connected = False self._connected = False
def _mqtt_connect(self, client, userdata, flags, rc): def _mqtt_connect(self, client, userdata, flags, rc):
logger.debug('Client on_connect called.') logger.debug('Client on_connect called.')
# Check if the result code is success (0) or some error (non-zero) and # Check if the result code is success (0) or some error (non-zero) and
@ -75,7 +77,7 @@ class MQTTClient(object):
self._connected = True self._connected = True
print('Connected to Adafruit IO!') print('Connected to Adafruit IO!')
else: else:
# handle RC errors within `errors.py`'s MQTTError class # handle RC errors within MQTTError class
raise MQTTError(rc) raise MQTTError(rc)
# Call the on_connect callback if available. # Call the on_connect callback if available.
if self.on_connect is not None: if self.on_connect is not None:
@ -88,6 +90,7 @@ class MQTTClient(object):
# log the RC as an error. Continue on to call any disconnect handler # log the RC as an error. Continue on to call any disconnect handler
# so clients can potentially recover gracefully. # so clients can potentially recover gracefully.
if rc != 0: if rc != 0:
print("Unexpected disconnection.")
raise MQTTError(rc) raise MQTTError(rc)
print('Disconnected from Adafruit IO!') print('Disconnected from Adafruit IO!')
# Call the on_disconnect callback if available. # Call the on_disconnect callback if available.
@ -99,7 +102,7 @@ class MQTTClient(object):
# Parse out the feed id and call on_message callback. # Parse out the feed id and call on_message callback.
# Assumes topic looks like "username/feeds/id" # Assumes topic looks like "username/feeds/id"
parsed_topic = msg.topic.split('/') parsed_topic = msg.topic.split('/')
if self.on_message is not None and self._username == parsed_topic[0]: if self.on_message is not None:
feed = parsed_topic[2] feed = parsed_topic[2]
payload = '' if msg.payload is None else msg.payload.decode('utf-8') payload = '' if msg.payload is None else msg.payload.decode('utf-8')
elif self.on_message is not None and parsed_topic[0] == 'time': elif self.on_message is not None and parsed_topic[0] == 'time':
@ -107,6 +110,10 @@ class MQTTClient(object):
payload = msg.payload.decode('utf-8') payload = msg.payload.decode('utf-8')
self.on_message(self, feed, payload) self.on_message(self, feed, payload)
def _mqtt_subscribe(client, userdata, mid, granted_qos):
"""Called when broker responds to a subscribe request."""
def connect(self, **kwargs): def connect(self, **kwargs):
"""Connect to the Adafruit.IO service. Must be called before any loop """Connect to the Adafruit.IO service. Must be called before any loop
or publish operations are called. Will raise an exception if a or publish operations are called. Will raise an exception if a
@ -162,16 +169,24 @@ class MQTTClient(object):
""" """
self._client.loop(timeout=timeout_sec) self._client.loop(timeout=timeout_sec)
def subscribe(self, feed_id): def subscribe(self, feed_id, feed_user=None):
"""Subscribe to changes on the specified feed. When the feed is updated """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_id and new value.
Params:
- feed_id: The id of the feed to update.
- feed_user (optional): The user id of the feed. Used for feed sharing.
""" """
self._client.subscribe('{0}/feeds/{1}'.format(self._username, feed_id)) if feed_user is not None:
(res, mid) = self._client.subscribe('{0}/feeds/{1}'.format(feed_user, feed_id))
else:
(res, mid) = self._client.subscribe('{0}/feeds/{1}'.format(self._username, feed_id))
return res, mid
def subscribe_time(self, time): def subscribe_time(self, time):
"""Subscribe to changes on the Adafruit IO time feeds. When the feed is """Subscribe to changes on the Adafruit IO time feeds. When the feed is
updated, the on_message function will be called and publish a new value: updated, the on_message function will be called and publish a new value:
time = time feeds:
millis: milliseconds millis: milliseconds
seconds: seconds seconds: seconds
iso: ISO-8601 (https://en.wikipedia.org/wiki/ISO_8601) iso: ISO-8601 (https://en.wikipedia.org/wiki/ISO_8601)
@ -181,15 +196,27 @@ class MQTTClient(object):
elif time == 'iso': elif time == 'iso':
self._client.subscribe('time/ISO-8601') self._client.subscribe('time/ISO-8601')
else: else:
print('ERROR: Invalid time type specified') raise TypeError('Invalid Time Feed Specified.')
return return
def publish(self, feed_id, value): def unsubscribe(self, feed_id):
"""Unsubscribes from a specified MQTT feed.
Note: this does not prevent publishing to a feed, it will unsubscribe
from receiving messages via on_message.
"""
(res, mid) = self._client.unsubscribe('{0}/feeds/{1}'.format(self._username, feed_id))
def publish(self, feed_id, value=None, feed_user=None):
"""Publish a value to a specified feed. """Publish a value to a specified feed.
Required parameters: Params:
- feed_id: The id of the feed to update. - feed_id: The id of the feed to update.
- feed_user (optional): The user id of the feed. Used for feed sharing.
- value: The new value to publish to the feed. - value: The new value to publish to the feed.
""" """
self._client.publish('{0}/feeds/{1}'.format(self._username, feed_id), if feed_user is not None:
payload=value) (res, self._pub_mid) = self._client.publish('{0}/feeds/{1}'.format(feed_user, feed_id),
payload=value)
else:
(res, self._pub_mid) = self._client.publish('{0}/feeds/{1}'.format(self._username, feed_id),
payload=value)

14
docs/feed-sharing.rst Normal file
View file

@ -0,0 +1,14 @@
Feed Sharing
------------
Feed sharing is a feature of Adafruit IO which allows you to share your feeds with people you specify.
If you want to share a feed on your Adafruit IO Account with another user, visit the `Sharing a feed page <https://learn.adafruit.com/adafruit-io-basics-feeds/sharing-a-feed>`_
on the Adafruit Learning System.
The Adafruit IO Python client supports Feed Sharing in the mqtt_client.py class.
Usage Example
~~~~~~~~~~~~~
.. literalinclude:: ../examples/mqtt/mqtt_shared_feeds.py

View file

@ -22,6 +22,7 @@ Table of Contents
:maxdepth: 6 :maxdepth: 6
feeds feeds
feed-sharing
data data
groups groups

View file

@ -0,0 +1,74 @@
"""
`mqtt_shared_feeds.py`
---------------------------------------------------------
Example of reading and writing to a shared Adafruit IO Feed.
learn.adafruit.com/adafruit-io-basics-feeds/sharing-a-feed
Author: Brent Rubell for Adafruit Industries 2018
"""
# Import standard python modules.
import sys
import time
import random
# Import Adafruit IO MQTT client.
from Adafruit_IO import MQTTClient
# 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'
# Shared IO Feed
# Make sure you have read AND write access to this feed to publish.
IO_FEED = 'SHARED_AIO_FEED_NAME'
# IO Feed Owner's username
IO_FEED_USERNAME = 'SHARED_AIO_FEED_USERNAME'
# Define callback functions which will be called when certain events happen.
def connected(client):
"""Connected function will be called when the client connects.
"""
client.subscribe(IO_FEED, IO_FEED_USERNAME)
def disconnected(client):
"""Disconnected function will be called when the client disconnects.
"""
print('Disconnected from Adafruit IO!')
sys.exit(1)
def message(client, feed_id, payload):
"""Message function will be called when a subscribed feed has a new value.
The feed_id parameter identifies the feed, and the payload parameter has
the new value.
"""
print('Feed {0} received new value: {1}'.format(feed_id, payload))
# Create an MQTT client instance.
client = MQTTClient(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
# Setup the callback functions defined above.
client.on_connect = connected
client.on_disconnect = disconnected
client.on_message = message
# Connect to the Adafruit IO server.
client.connect()
client.loop_background()
print('Publishing a new message every 10 seconds (press Ctrl-C to quit)...')
while True:
value = random.randint(0, 100)
print('Publishing {0} to {1}.'.format(value, IO_FEED))
client.publish(IO_FEED, value, IO_FEED_USERNAME)
time.sleep(10)