diff --git a/Adafruit_IO/_version.py b/Adafruit_IO/_version.py index ab999f0..00b64fe 100644 --- a/Adafruit_IO/_version.py +++ b/Adafruit_IO/_version.py @@ -1 +1 @@ -__version__ = "2.0.18" \ No newline at end of file +__version__ = "2.0.19" \ No newline at end of file diff --git a/Adafruit_IO/client.py b/Adafruit_IO/client.py index 76d603b..bd1ceb2 100644 --- a/Adafruit_IO/client.py +++ b/Adafruit_IO/client.py @@ -61,10 +61,10 @@ def __init__(self, username, key, proxies=None, base_url='https://io.adafruit.co self.base_url = base_url.rstrip('/') def _compose_url(self, path, is_time=None): - if not is_time: + if is_time: # return a call to https://io.adafruit.com/api/v2/time/{unit} + return '{0}/api/{1}/{2}'.format(self.base_url, self.api_version, path) + else: return '{0}/api/{1}/{2}/{3}'.format(self.base_url, self.api_version, self.username, path) - else: # return a call to https://io.adafruit.com/api/v2/time/{unit} - return '{0}/api/{1}/{2}'.format(self.base_url, self.api_version, path) def _handle_error(self, response): @@ -161,6 +161,16 @@ def receive_time(self, time): """ timepath = "time/{0}".format(time) return self._get(timepath, is_time=True) + + def receive_weather(self, weather_id=None): + """Adafruit IO Weather Service, Powered by Dark Sky + :param int id: optional ID for retrieving a specified weather record. + """ + if weather_id: + weatherpath = "integrations/weather/{0}".format(weather_id) + else: + weatherpath = "integrations/weather" + return self._get(weatherpath) def receive(self, feed): """Retrieve the most recent value for the specified feed. Returns a Data diff --git a/Adafruit_IO/mqtt_client.py b/Adafruit_IO/mqtt_client.py index c032dae..046669a 100644 --- a/Adafruit_IO/mqtt_client.py +++ b/Adafruit_IO/mqtt_client.py @@ -29,6 +29,11 @@ logger = logging.getLogger(__name__) +forecast_types = ["current", "forecast_minutes_5", + "forecast_minutes_30", "forecast_hours_1", + "forecast_hours_2", "forecast_hours_6", + "forecast_hours_24", "forecast_days_1", + "forecast_days_2", "forecast_days_5",] class MQTTClient(object): """Interface for publishing and subscribing to feed changes on Adafruit IO @@ -103,8 +108,8 @@ def _mqtt_message(self, client, userdata, msg): assume topic looks like `username/topic/id` """ parsed_topic = msg.topic.split('/') - if self.on_message is not None: - topic = parsed_topic[2] + if self.on_message is not None and parsed_topic[2] == 'weather': + topic = parsed_topic[4] # parse out the forecast type payload = '' if msg.payload is None else msg.payload.decode('utf-8') elif self.on_message is not None and parsed_topic[0] == 'time': topic = parsed_topic[0] @@ -112,6 +117,9 @@ def _mqtt_message(self, client, userdata, msg): elif self.on_message is not None and parsed_topic[1] == 'groups': topic = parsed_topic[3] payload = msg.payload.decode('utf-8') + else: # default topic + topic = parsed_topic[2] + payload = '' if msg.payload is None else msg.payload.decode('utf-8') self.on_message(self, topic, payload) def _mqtt_subscribe(client, userdata, mid, granted_qos): @@ -197,6 +205,17 @@ def subscribe_group(self, group_id): """ self._client.subscribe('{0}/groups/{1}'.format(self._username, group_id)) + def subscribe_weather(self, weather_id, forecast_type): + """Subscribe to Adafruit IO Weather + :param int weather_id: weather record you want data for + :param string type: type of forecast data requested + """ + if forecast_type in forecast_types: + self._client.subscribe('{0}/integration/weather/{1}/{2}'.format(self._username, weather_id, forecast_type)) + else: + raise TypeError("Invalid Forecast Type Specified.") + return + def subscribe_time(self, time): """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: diff --git a/examples/basics/digital_in.py b/examples/basics/digital_in.py deleted file mode 100644 index d2fdb4e..0000000 --- a/examples/basics/digital_in.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -'digital_in.py' -================================== -Example of sending button values -to an Adafruit IO feed. - -Author(s): Brent Rubell, Todd Treece -""" -# Import standard python modules -import time - -# import Adafruit Blinka -import board -import digitalio - -# 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' - -# Create an instance of the REST client. -aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY) - -try: # if we have a 'digital' feed - digital = aio.feeds('digital') -except RequestError: # create a digital feed - feed = Feed(name="digital") - digital = aio.create_feed(feed) - -# button set up -button = digitalio.DigitalInOut(board.D12) -button.direction = digitalio.Direction.INPUT -button.pull = digitalio.Pull.UP -button_current = 0 - - -while True: - if not button.value: - button_current = 1 - else: - button_current = 0 - - print('Button -> ', button_current) - aio.send(digital.key, button_current) - - # avoid timeout from adafruit io - time.sleep(1) diff --git a/examples/mqtt/mqtt_weather.py b/examples/mqtt/mqtt_weather.py new file mode 100644 index 0000000..9d148d4 --- /dev/null +++ b/examples/mqtt/mqtt_weather.py @@ -0,0 +1,125 @@ +""" +Example of using the Adafruit IO MQTT Client +for subscribing to the Adafruit IO Weather Service +Note: This feature is avaliable for IO Plus Subscribers ONLY + +API Documentation: https://io.adafruit.com/services/weather + +Author: Brent Rubell for Adafruit Industries +""" + +# Import standard python modules. +import sys +import json + +# 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 = 'KEY' + +# Set to your Adafruit IO username. +# (go to https://accounts.adafruit.com to find your username) +ADAFRUIT_IO_USERNAME = 'USER' + +# Set to ID of the forecast to subscribe to for updates +forecast_id = 2153 + +# Set to the ID of the feed to subscribe to for updates. +""" +Valid forecast types are: +current +forecast_minutes_5 +forecast_minutes_30 +forecast_hours_1 +forecast_hours_2 +forecast_hours_6 +forecast_hours_24 +forecast_days_1 +forecast_days_2 +forecast_days_5 +""" +# Subscribe to the current forecast +forecast_today = 'current' +# Subscribe to tomorrow's forecast +forecast_two_days = 'forecast_days_2' +# Subscribe to forecast in 5 days +forecast_in_5_days = 'forecast_days_5' + +# Define callback functions which will be called when certain events happen. +# pylint: disable=redefined-outer-name +def connected(client): + # Connected function will be called when the client is connected to Adafruit IO. + # This is a good place to subscribe to feed changes. The client parameter + # passed to this function is the Adafruit IO MQTT client so you can make + # calls against it easily. + print('Connected to Adafruit IO! Listening to forecast: {0}...'.format(forecast_id)) + # Subscribe to changes on the current forecast. + client.subscribe_weather(forecast_id, forecast_today) + + # Subscribe to changes on tomorrow's forecast. + client.subscribe_weather(forecast_id, forecast_two_days) + + # Subscribe to changes on forecast in 5 days. + client.subscribe_weather(forecast_id, forecast_in_5_days) + +# pylint: disable=unused-argument +def disconnected(client): + # Disconnected function will be called when the client disconnects. + print('Disconnected from Adafruit IO!') + sys.exit(1) + +# pylint: disable=unused-argument +def message(client, topic, payload): + """Message function will be called when any subscribed forecast has an update. + Weather data is updated at most once every 20 minutes. + """ + # forecast based on mqtt topic + if topic == 'current': + # Print out today's forecast + today_forecast = payload + print('\nCurrent Forecast') + parseForecast(today_forecast) + elif topic == 'forecast_days_2': + # Print out tomorrow's forecast + two_day_forecast = payload + print('\nWeather in Two Days') + parseForecast(two_day_forecast) + elif topic == 'forecast_days_5': + # Print out forecast in 5 days + five_day_forecast = payload + print('\nWeather in 5 Days') + parseForecast(five_day_forecast) + +def parseForecast(forecast_data): + """Parses and prints incoming forecast data + """ + # incoming data is a utf-8 string, encode it as a json object + forecast = json.loads(forecast_data) + # Print out the forecast + try: + print('It is {0} and {1}F.'.format(forecast['summary'], forecast['temperature'])) + except KeyError: + # future weather forecasts return a high and low temperature, instead of 'temperature' + print('It will be {0} with a high of {1}F and a low of {2}F.'.format( + forecast['summary'], forecast['temperatureLow'], forecast['temperatureHigh'])) + print('with humidity of {0}%, wind speed of {1}mph, and {2}% chance of precipitation.'.format( + forecast['humidity'], forecast['windSpeed'], forecast['precipProbability'])) + +# 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() + +# Start a message loop that blocks forever waiting for MQTT messages to be +# received. Note there are other options for running the event loop like doing +# so in a background thread--see the mqtt_client.py example to learn more. +client.loop_blocking()