20
20
"""
21
21
import time
22
22
import json
23
+ import re
23
24
24
25
from adafruit_io .adafruit_io_errors import (
25
26
AdafruitIO_RequestError ,
33
34
CLIENT_HEADERS = {"User-Agent" : "AIO-CircuitPython/{0}" .format (__version__ )}
34
35
35
36
37
+ def validate_feed_key (feed_key ):
38
+ """Validates a provided feed key against Adafruit IO's system rules.
39
+ https://learn.adafruit.com/naming-things-in-adafruit-io/the-two-feed-identifiers
40
+ """
41
+ if len (feed_key ) > 128 : # validate feed key length
42
+ raise ValueError ("Feed key must be less than 128 characters." )
43
+ if not bool (re .match ("^[a-z0-9-]+$" , feed_key )): # validate key naming scheme
44
+ raise TypeError (
45
+ "Feed key must contain lower case English letters, numbers, and dash."
46
+ )
47
+
48
+
36
49
class IO_MQTT :
37
50
"""
38
51
Client for interacting with Adafruit IO MQTT API.
@@ -186,6 +199,7 @@ def add_feed_callback(self, feed_key, callback_method):
186
199
:param str callback_method: Name of callback method.
187
200
188
201
"""
202
+ validate_feed_key (feed_key )
189
203
self ._client .add_topic_callback (
190
204
"{0}/f/{1}" .format (self ._user , feed_key ), callback_method
191
205
)
@@ -199,6 +213,7 @@ def remove_feed_callback(self, feed_key):
199
213
:param str feed_key: Adafruit IO feed key.
200
214
201
215
"""
216
+ validate_feed_key (feed_key )
202
217
self ._client .remove_topic_callback ("{0}/f/{1}" .format (self ._user , feed_key ))
203
218
204
219
def loop (self ):
@@ -235,6 +250,7 @@ def subscribe(self, feed_key=None, group_key=None, shared_user=None):
235
250
236
251
client.subscribe([('temperature'), ('humidity')])
237
252
"""
253
+ validate_feed_key (feed_key )
238
254
if shared_user is not None and feed_key is not None :
239
255
self ._client .subscribe ("{0}/f/{1}" .format (shared_user , feed_key ))
240
256
elif group_key is not None :
@@ -314,6 +330,7 @@ def unsubscribe(self, feed_key=None, group_key=None, shared_user=None):
314
330
client.unsubscribe('temperature', shared_user='adabot')
315
331
316
332
"""
333
+ validate_feed_key (feed_key )
317
334
if shared_user is not None and feed_key is not None :
318
335
self ._client .unsubscribe ("{0}/f/{1}" .format (shared_user , feed_key ))
319
336
elif group_key is not None :
@@ -398,6 +415,7 @@ def publish(self, feed_key, data, metadata=None, shared_user=None, is_group=Fals
398
415
io.publish("location-feed", data, metadata)
399
416
400
417
"""
418
+ validate_feed_key (feed_key )
401
419
if is_group :
402
420
self ._client .publish ("{0}/g/{1}" .format (self ._user , feed_key ), data )
403
421
if shared_user is not None :
@@ -423,6 +441,7 @@ def get(self, feed_key):
423
441
424
442
io.get('temperature')
425
443
"""
444
+ validate_feed_key (feed_key )
426
445
self ._client .publish ("{0}/f/{1}/get" .format (self ._user , feed_key ), "\0 " )
427
446
428
447
@@ -534,6 +553,7 @@ def send_data(self, feed_key, data, metadata=None, precision=None):
534
553
:param dict metadata: Optional metadata associated with the data
535
554
:param int precision: Optional amount of precision points to send with floating point data
536
555
"""
556
+ validate_feed_key (feed_key )
537
557
path = self ._compose_path ("feeds/{0}/data" .format (feed_key ))
538
558
if precision :
539
559
try :
@@ -551,6 +571,7 @@ def receive_all_data(self, feed_key):
551
571
returned in reverse order.
552
572
:param str feed_key: Adafruit IO feed key
553
573
"""
574
+ validate_feed_key (feed_key )
554
575
path = self ._compose_path ("feeds/{0}/data" .format (feed_key ))
555
576
return self ._get (path )
556
577
@@ -559,6 +580,7 @@ def receive_data(self, feed_key):
559
580
Return the most recent value for the specified feed.
560
581
:param string feed_key: Adafruit IO feed key
561
582
"""
583
+ validate_feed_key (feed_key )
562
584
path = self ._compose_path ("feeds/{0}/data/last" .format (feed_key ))
563
585
return self ._get (path )
564
586
@@ -568,6 +590,7 @@ def delete_data(self, feed_key, data_id):
568
590
:param string feed: Adafruit IO feed key
569
591
:param string data_id: Data point to delete from the feed
570
592
"""
593
+ validate_feed_key (feed_key )
571
594
path = self ._compose_path ("feeds/{0}/data/{1}" .format (feed_key , data_id ))
572
595
return self ._delete (path )
573
596
@@ -614,6 +637,7 @@ def add_feed_to_group(self, group_key, feed_key):
614
637
:param str group_key: Group
615
638
:param str feed_key: Feed to add to the group
616
639
"""
640
+ validate_feed_key (feed_key )
617
641
path = self ._compose_path ("groups/{0}/add" .format (group_key ))
618
642
payload = {"feed_key" : feed_key }
619
643
return self ._post (path , payload )
@@ -625,6 +649,7 @@ def get_feed(self, feed_key, detailed=False):
625
649
:param str feed_key: Adafruit IO Feed Key
626
650
:param bool detailed: Returns a more verbose feed record
627
651
"""
652
+ validate_feed_key (feed_key )
628
653
if detailed :
629
654
path = self ._compose_path ("feeds/{0}/details" .format (feed_key ))
630
655
else :
@@ -638,6 +663,7 @@ def create_new_feed(self, feed_key, feed_desc=None, feed_license=None):
638
663
:param str feed_desc: Optional description of feed
639
664
:param str feed_license: Optional feed license
640
665
"""
666
+ validate_feed_key (feed_key )
641
667
path = self ._compose_path ("feeds" )
642
668
payload = {"name" : feed_key , "description" : feed_desc , "license" : feed_license }
643
669
return self ._post (path , payload )
@@ -647,6 +673,7 @@ def delete_feed(self, feed_key):
647
673
Deletes an existing feed.
648
674
:param str feed_key: Valid feed key
649
675
"""
676
+ validate_feed_key (feed_key )
650
677
path = self ._compose_path ("feeds/{0}" .format (feed_key ))
651
678
return self ._delete (path )
652
679
0 commit comments