diff --git a/.gitignore b/.gitignore index 41278df..8c1d213 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ setuptools-* dist build eggs +.eggs parts bin var diff --git a/Adafruit_IO/_version.py b/Adafruit_IO/_version.py index 239bf2b..528787c 100644 --- a/Adafruit_IO/_version.py +++ b/Adafruit_IO/_version.py @@ -1 +1 @@ -__version__ = "2.8.2" +__version__ = "3.0.0" diff --git a/Adafruit_IO/client.py b/Adafruit_IO/client.py index 3f92326..9562aa7 100644 --- a/Adafruit_IO/client.py +++ b/Adafruit_IO/client.py @@ -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 @@ -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(), diff --git a/Adafruit_IO/mqtt_client.py b/Adafruit_IO/mqtt_client.py index 198b4d6..ef9a53e 100644 --- a/Adafruit_IO/mqtt_client.py +++ b/Adafruit_IO/mqtt_client.py @@ -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 @@ -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. @@ -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): @@ -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) diff --git a/examples/api/data.py b/examples/api/data.py index 63df954..4805e31 100644 --- a/examples/api/data.py +++ b/examples/api/data.py @@ -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) diff --git a/examples/api/feeds.py b/examples/api/feeds.py index 848bdda..1047498 100644 --- a/examples/api/feeds.py +++ b/examples/api/feeds.py @@ -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) diff --git a/examples/api/location.py b/examples/api/location.py index 3e5846a..b2cf2cf 100644 --- a/examples/api/location.py +++ b/examples/api/location.py @@ -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) diff --git a/examples/api/random_data.py b/examples/api/random_data.py index 719e026..edda90c 100644 --- a/examples/api/random_data.py +++ b/examples/api/random_data.py @@ -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) diff --git a/examples/api/simple.py b/examples/api/simple.py index 2f7de33..2ec5326 100644 --- a/examples/api/simple.py +++ b/examples/api/simple.py @@ -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) diff --git a/examples/api/weather.py b/examples/api/weather.py index 3e4bc5c..7307821 100644 --- a/examples/api/weather.py +++ b/examples/api/weather.py @@ -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) @@ -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'])) \ No newline at end of file +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"], + ) +) diff --git a/examples/basics/analog_in.py b/examples/basics/analog_in.py index 14f73f4..5b0e75f 100644 --- a/examples/basics/analog_in.py +++ b/examples/basics/analog_in.py @@ -13,6 +13,7 @@ (https://github.com/adafruit/Adafruit_CircuitPython_MCP3xxx) """ # Import standard python modules +import os import time # import Adafruit Blinka @@ -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) diff --git a/examples/basics/analog_output.py b/examples/basics/analog_output.py index a1d2994..f04a1e8 100644 --- a/examples/basics/analog_output.py +++ b/examples/basics/analog_output.py @@ -22,6 +22,7 @@ """ # import system libraries +import os import time # import Adafruit Blinka @@ -34,14 +35,14 @@ # 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_IO_KEY' - # Set to your Adafruit IO username. # (go to https://accounts.adafruit.com to find your username) -ADAFRUIT_IO_USERNAME = 'YOUR_IO_USERNAME' +ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_USERNAME', 'YOUR_IO_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_IO_KEY') # Create the I2C bus interface. i2c_bus = I2C(SCL, SDA) diff --git a/examples/basics/dashboard.py b/examples/basics/dashboard.py index 8a5bc61..a2619bd 100644 --- a/examples/basics/dashboard.py +++ b/examples/basics/dashboard.py @@ -5,18 +5,19 @@ Author(s): Doug Zobel """ +import os from time import sleep from random import randrange from Adafruit_IO import Client, Feed, Block, Dashboard, Layout -# 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_USERNAME = '' - # Set to your Adafruit IO username. # (go to https://accounts.adafruit.com to find your username) -ADAFRUIT_IO_KEY = '' +ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_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', '') # Create an instance of the REST client. aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY) @@ -27,7 +28,7 @@ # Fetch group info (group.id needed when adding feeds to blocks) group = aio.groups("default") -# Create a new dasbhoard named 'Example Dashboard' +# Create a new dashboard named 'Example Dashboard' dashboard = aio.create_dashboard(Dashboard(name="Example Dashboard")) # Create a line_chart @@ -36,6 +37,7 @@ properties = { "gridLines": True, "historyHours": "2"}, + # block_feeds expects a numeric feed_id, not the feed key block_feeds = [{ "group_id": group.id, "feed_id": feed.id diff --git a/examples/basics/digital_in.py b/examples/basics/digital_in.py index c4a40c0..d83cd19 100644 --- a/examples/basics/digital_in.py +++ b/examples/basics/digital_in.py @@ -7,6 +7,7 @@ Author(s): Brent Rubell, Todd Treece """ # Import standard python modules +import os import time # import Adafruit Blinka @@ -16,14 +17,14 @@ # 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) diff --git a/examples/basics/digital_out.py b/examples/basics/digital_out.py index 5da4f99..4773693 100644 --- a/examples/basics/digital_out.py +++ b/examples/basics/digital_out.py @@ -7,6 +7,7 @@ Author(s): Brent Rubell, Todd Treece """ # Import standard python modules +import os import time # import Adafruit Blinka @@ -16,14 +17,14 @@ # 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) @@ -42,7 +43,7 @@ while True: try: data = aio.receive(digital.key) - except RequestError as re: + except RequestError: pass # feed with no data will return 404 if int(data.value) == 1: print('received <- ON\n') diff --git a/examples/basics/location.py b/examples/basics/location.py index c772a9c..a4688d0 100644 --- a/examples/basics/location.py +++ b/examples/basics/location.py @@ -7,19 +7,20 @@ Author(s): Brent Rubell, Todd Treece """ # Import standard python modules +import os import time # 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) diff --git a/examples/basics/neopixel.py b/examples/basics/neopixel.py index 5c6b241..b74067b 100644 --- a/examples/basics/neopixel.py +++ b/examples/basics/neopixel.py @@ -20,6 +20,7 @@ - Adafruit_CircuitPython_NeoPixel (https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel) """ +import os import time import board import neopixel @@ -39,14 +40,14 @@ pixel_pin, num_pixels, brightness=0.2, auto_write=False, pixel_order=ORDER ) -# 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) diff --git a/examples/basics/pi_camera.py b/examples/basics/pi_camera.py index 4fd26ec..c8e18ce 100644 --- a/examples/basics/pi_camera.py +++ b/examples/basics/pi_camera.py @@ -6,12 +6,12 @@ Adafruit IO feed. """ # import standard python modules -import time import base64 import os +import time # import Adafruit IO REST client -from Adafruit_IO import Client, Feed, RequestError +from Adafruit_IO import Client # import raspberry pi camera module import picamera @@ -19,14 +19,14 @@ # camera capture interval, in seconds CAMERA_INTERVAL = 3 -# 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) @@ -56,7 +56,8 @@ try: aio.send(picam_feed.key, image_string) print('Picture sent to Adafruit IO') - except: + except Exception as e: print('Sending to Adafruit IO Failed...') + print('Error:', e) time.sleep(CAMERA_INTERVAL) diff --git a/examples/basics/publish.py b/examples/basics/publish.py index 6498ee3..47984f7 100644 --- a/examples/basics/publish.py +++ b/examples/basics/publish.py @@ -6,6 +6,7 @@ Author(s): Brent Rubell, Todd Treece for Adafruit Industries """ # Import standard python modules +import os import time # Import Adafruit IO REST client. @@ -14,14 +15,14 @@ # holds the count for the feed run_count = 0 -# 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) diff --git a/examples/basics/rgb_led.py b/examples/basics/rgb_led.py index 06781bf..9ae06cf 100644 --- a/examples/basics/rgb_led.py +++ b/examples/basics/rgb_led.py @@ -22,6 +22,7 @@ (https://github.com/adafruit/Adafruit_CircuitPython_PCA9685) """ # import system libraries +import os import time # import Adafruit Blinka @@ -39,14 +40,14 @@ GREEN_PIN = 5 BLUE_PIN = 4 -# 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) diff --git a/examples/basics/servo.py b/examples/basics/servo.py index 54ee95f..f1fe086 100644 --- a/examples/basics/servo.py +++ b/examples/basics/servo.py @@ -24,6 +24,7 @@ """ # import system libraries +import os import time # import Adafruit Blinka @@ -41,12 +42,12 @@ # 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 = 'YOUR_AIO_KEY' +# 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) diff --git a/examples/basics/subscribe.py b/examples/basics/subscribe.py index f9661f6..f80d153 100644 --- a/examples/basics/subscribe.py +++ b/examples/basics/subscribe.py @@ -6,22 +6,23 @@ Author(s): Brent Rubell, Todd Treece for Adafruit Industries """ # Import standard python modules. +import os import sys # This example uses the MQTTClient instead of the REST 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' +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') -# Set to the ID of the feed to subscribe to for updates. -FEED_ID = 'counter' +# Set to the key of the feed to subscribe to for updates. +FEED_KEY = 'counter' # Define callback functions which will be called when certain events happen. def connected(client): @@ -31,20 +32,20 @@ def connected(client): can make calls against it easily. """ # Subscribe to changes on a feed named Counter. - print('Subscribing to Feed {0}'.format(FEED_ID)) - client.subscribe(FEED_ID) + print('Subscribing to Feed {0}'.format(FEED_KEY)) + client.subscribe(FEED_KEY) print('Waiting for feed data...') def disconnected(client): """Disconnected function will be called when the client disconnects.""" sys.exit(1) -def message(client, feed_id, payload): +def message(client, feed_key, 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 feed_key parameter identifies the feed, and the payload parameter has the new value. """ - print('Feed {0} received new value: {1}'.format(feed_id, payload)) + print('Feed {0} received new value: {1}'.format(feed_key, payload)) # Create an MQTT client instance. diff --git a/examples/basics/temp_humidity.py b/examples/basics/temp_humidity.py index 618d5f6..3d76894 100644 --- a/examples/basics/temp_humidity.py +++ b/examples/basics/temp_humidity.py @@ -15,6 +15,7 @@ """ # import standard python modules. +import os import time # import adafruit-blinka modules @@ -32,14 +33,14 @@ # Time between sensor reads, in seconds READ_TIMEOUT = 60 -# 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) @@ -71,7 +72,7 @@ print('Temp={0:0.1f}*F'.format(temperature)) else: print('Temp={0:0.1f}*C'.format(temperature)) - print('Humidity={1:0.1f}%'.format(humidity)) + print('Humidity={0:0.1f}%'.format(humidity)) # Format sensor data as string for sending to Adafruit IO temperature = '%.2f'%(temperature) humidity = '%.2f'%(humidity) diff --git a/examples/basics/time.py b/examples/basics/time.py index a6204c8..a29349c 100644 --- a/examples/basics/time.py +++ b/examples/basics/time.py @@ -9,17 +9,20 @@ Author: Brent Rubell """ -# Import Adafruit IO REST client. -from Adafruit_IO import Client, Feed, Data, RequestError +# Import standard python modules. +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 # 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) diff --git a/examples/basics/type-conversion.py b/examples/basics/type-conversion.py index 4ccb729..86a6647 100644 --- a/examples/basics/type-conversion.py +++ b/examples/basics/type-conversion.py @@ -8,17 +8,20 @@ Author(s): Brent Rubell, Todd Treece for Adafruit Industries """ +# 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') aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY) diff --git a/examples/mqtt/mqtt_client_class.py b/examples/mqtt/mqtt_client_class.py index e7a16ff..583c7ad 100644 --- a/examples/mqtt/mqtt_client_class.py +++ b/examples/mqtt/mqtt_client_class.py @@ -2,6 +2,7 @@ # Author: Tony DiCola # Import standard python modules. +import os import random import sys import time @@ -9,14 +10,14 @@ # 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' +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') # Define callback functions which will be called when certain events happen. @@ -34,11 +35,11 @@ def disconnected(client): print('Disconnected from Adafruit IO!') sys.exit(1) -def message(client, feed_id, payload): +def message(client, feed_key, 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 feed_key parameter identifies the feed, and the payload parameter has # the new value. - print('Feed {0} received new value: {1}'.format(feed_id, payload)) + print('Feed {0} received new value: {1}'.format(feed_key, payload)) # Create an MQTT client instance. diff --git a/examples/mqtt/mqtt_groups_pubsub.py b/examples/mqtt/mqtt_groups_pubsub.py index cf69963..27241ed 100644 --- a/examples/mqtt/mqtt_groups_pubsub.py +++ b/examples/mqtt/mqtt_groups_pubsub.py @@ -4,6 +4,7 @@ # Author: Brent Rubell for Adafruit Industries, 2018 # Import standard python modules. +import os import random import sys import time @@ -11,14 +12,14 @@ # 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' +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') # Group Name group_name = 'grouptest' diff --git a/examples/mqtt/mqtt_shared_feeds.py b/examples/mqtt/mqtt_shared_feeds.py index 23b2f16..bdc3cc2 100644 --- a/examples/mqtt/mqtt_shared_feeds.py +++ b/examples/mqtt/mqtt_shared_feeds.py @@ -9,6 +9,7 @@ """ # Import standard python modules. +import os import sys import time import random @@ -16,14 +17,14 @@ # 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' +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') # Shared IO Feed # Make sure you have read AND write access to this feed to publish. @@ -45,12 +46,12 @@ def disconnected(client): print('Disconnected from Adafruit IO!') sys.exit(1) -def message(client, feed_id, payload): +def message(client, feed_key, 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 feed_key parameter identifies the feed, and the payload parameter has the new value. """ - print('Feed {0} received new value: {1}'.format(feed_id, payload)) + print('Feed {0} received new value: {1}'.format(feed_key, payload)) # Create an MQTT client instance. diff --git a/examples/mqtt/mqtt_subscribe.py b/examples/mqtt/mqtt_subscribe.py index e3b2999..0b92471 100644 --- a/examples/mqtt/mqtt_subscribe.py +++ b/examples/mqtt/mqtt_subscribe.py @@ -3,22 +3,24 @@ # username, and feed to subscribe to for changes. # Import standard python modules. +import os import sys # 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' +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') -# Set to the ID of the feed to subscribe to for updates. -FEED_ID = 'DemoFeed' +# Set to the key of the feed to subscribe to for updates. +# Create this feed in your Adafruit IO account and then publish some values +FEED_KEY = 'DemoFeed' # Define callback functions which will be called when certain events happen. @@ -27,24 +29,28 @@ def connected(client): # 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 for {0} changes...'.format(FEED_ID)) + print( + "Connected to Adafruit IO as {0}! Listening for {1} changes...".format( + ADAFRUIT_IO_USERNAME, FEED_KEY + ) + ) # Subscribe to changes on a feed named DemoFeed. - client.subscribe(FEED_ID) + client.subscribe(FEED_KEY) def subscribe(client, userdata, mid, granted_qos): # This method is called when the client subscribes to a new feed. - print('Subscribed to {0} with QoS {1}'.format(FEED_ID, granted_qos[0])) + print('Subscribed to {0} with QoS {1}'.format(FEED_KEY, granted_qos[0])) 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): +def message(client, feed_key, 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 feed_key parameter identifies the feed, and the payload parameter has # the new value. - print('Feed {0} received new value: {1}'.format(feed_id, payload)) + print('Feed {0} received new value: {1}'.format(feed_key, payload)) # Create an MQTT client instance. diff --git a/examples/mqtt/mqtt_time.py b/examples/mqtt/mqtt_time.py index 19875d3..b643458 100644 --- a/examples/mqtt/mqtt_time.py +++ b/examples/mqtt/mqtt_time.py @@ -8,31 +8,32 @@ """ # Import standard python modules. +import os import sys import time # 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' +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') 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): +def message(client, feed_key, 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 feed_key parameter identifies the feed, and the payload parameter has # the new value. - print('\t Feed {0} received new value: {1}'.format(feed_id, payload)) + print('\t Feed {0} received new value: {1}'.format(feed_key, payload)) # Create a SECURE MQTT client instance diff --git a/examples/mqtt/mqtt_weather.py b/examples/mqtt/mqtt_weather.py index 9d148d4..244e64d 100644 --- a/examples/mqtt/mqtt_weather.py +++ b/examples/mqtt/mqtt_weather.py @@ -9,20 +9,21 @@ """ # Import standard python modules. -import sys import json +import os +import sys # 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' +ADAFRUIT_IO_USERNAME = os.getenv('ADAFRUIT_IO_USERNAME', 'USER') + +# 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', 'KEY') # Set to ID of the forecast to subscribe to for updates forecast_id = 2153 diff --git a/tests/test_mqtt_client.py b/tests/test_mqtt_client.py index fc611e9..6186bc0 100644 --- a/tests/test_mqtt_client.py +++ b/tests/test_mqtt_client.py @@ -22,6 +22,7 @@ import unittest from Adafruit_IO import MQTTClient +from Adafruit_IO.mqtt_client import validate_feed_key import base @@ -122,4 +123,124 @@ def on_message(mqtt_client, feed, payload): client.loop() time.sleep(0) # Verify one update message with payload is received. - self.assertListEqual(messages, [('testfeed', '42')]) \ No newline at end of file + self.assertListEqual(messages, [('testfeed', '42')]) + + +class TestFeedKeyValidation(unittest.TestCase): + """Tests for feed key validation.""" + + def test_valid_feed_keys(self): + """Test that valid feed keys pass validation.""" + valid_keys = [ + 'testfeed', + 'test-feed', + 'test123', + 'TESTFEED', + 'Test-Feed-123', + 'group.feed', + 'group/feed', + 'my-group.my-feed', + 'my-group/my-feed', + ] + for key in valid_keys: + try: + validate_feed_key(key) + except (ValueError, TypeError) as e: + self.fail(f"Valid feed key '{key}' raised exception: {e}") + + def test_empty_feed_key(self): + """Test that empty string raises TypeError due to invalid pattern.""" + with self.assertRaises(TypeError) as context: + validate_feed_key('') + self.assertIn('Feed key must contain', str(context.exception)) + + def test_feed_key_too_long(self): + """Test that feed keys longer than 128 characters raise ValueError.""" + long_key = 'a' * 129 + with self.assertRaises(ValueError) as context: + validate_feed_key(long_key) + self.assertIn('less than 128 characters', str(context.exception)) + + def test_feed_key_invalid_characters(self): + """Test that feed keys with invalid characters raise TypeError.""" + invalid_keys = [ + 'test feed', # space + 'test_feed', # underscore + 'test@feed', # special character + 'test!feed', # special character + 'test#feed', # special character + 'test$feed', # special character + ] + for key in invalid_keys: + with self.assertRaises(TypeError) as context: + validate_feed_key(key) + self.assertIn('Feed key must contain', str(context.exception)) + + def test_feed_key_invalid_patterns(self): + """Test that feed keys with invalid patterns raise TypeError.""" + invalid_keys = [ + '.testfeed', # starts with period + '/testfeed', # starts with slash + 'test.', # ends with period + 'test/', # ends with slash + 'test..feed', # double period + 'test//feed', # double slash + 'test./feed', # period followed by slash + 'test/.feed', # slash followed by period + ] + for key in invalid_keys: + with self.assertRaises(TypeError) as context: + validate_feed_key(key) + self.assertIn('Feed key must contain', str(context.exception)) + + +class TestMQTTClientFeedKeyValidation(unittest.TestCase): + """Tests for feed key validation in MQTT client methods.""" + + def test_subscribe_with_empty_feed_key(self): + """Test that subscribe() raises TypeError with empty feed key.""" + client = MQTTClient('testuser', 'testkey') + with self.assertRaises(TypeError): + client.subscribe('') + + def test_subscribe_with_invalid_feed_key(self): + """Test that subscribe() raises TypeError with invalid feed key.""" + client = MQTTClient('testuser', 'testkey') + with self.assertRaises(TypeError): + client.subscribe('invalid feed') + + def test_publish_with_empty_feed_key(self): + """Test that publish() raises TypeError with empty feed key.""" + client = MQTTClient('testuser', 'testkey') + with self.assertRaises(TypeError): + client.publish('', 42) + + def test_publish_with_invalid_feed_key(self): + """Test that publish() raises TypeError with invalid feed key.""" + client = MQTTClient('testuser', 'testkey') + with self.assertRaises(TypeError): + client.publish('invalid_feed', 42) + + def test_unsubscribe_with_empty_feed_key(self): + """Test that unsubscribe() raises TypeError with empty feed key.""" + client = MQTTClient('testuser', 'testkey') + with self.assertRaises(TypeError): + client.unsubscribe(feed_key='') + + def test_unsubscribe_with_invalid_feed_key(self): + """Test that unsubscribe() raises TypeError with invalid feed key.""" + client = MQTTClient('testuser', 'testkey') + with self.assertRaises(TypeError): + client.unsubscribe(feed_key='test feed') + + def test_receive_with_empty_feed_key(self): + """Test that receive() raises TypeError with empty feed key.""" + client = MQTTClient('testuser', 'testkey') + with self.assertRaises(TypeError): + client.receive('') + + def test_receive_with_invalid_feed_key(self): + """Test that receive() raises TypeError with invalid feed key.""" + client = MQTTClient('testuser', 'testkey') + with self.assertRaises(TypeError): + client.receive('test@feed') \ No newline at end of file