-
Notifications
You must be signed in to change notification settings - Fork 51
Add Topic-specific message callbacks #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
9fad312
add MQTTMatcher, expose MQTT's on_message, allow multiple callbacks
11e99d9
add licensing details
dda9157
fixup syntax, delete message from on_msg_filtered
a8b1af9
add new example
0a7c894
handle keyerror
fbe6fa0
fix api doc issue with automodule
6f3165d
fix on_message, author name per @jimbobbennett rvw
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# Copyright (c) 2017 Yotch <https://github.com/yoch> | ||
# | ||
# This file is dual licensed under the Eclipse Public License 1.0 and the | ||
# Eclipse Distribution License 1.0 as described in the epl-v10 and edl-v10 files. | ||
# | ||
# | ||
""" | ||
`matcher` | ||
==================================================================================== | ||
|
||
MQTT topic filter matcher from the Eclipse Project's Paho.MQTT.Python | ||
https://github.com/eclipse/paho.mqtt.python/blob/master/src/paho/mqtt/matcher.py | ||
* Author(s): Yotch (https://github.com/yoch) | ||
brentru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
|
||
|
||
class MQTTMatcher: | ||
"""Intended to manage topic filters including wildcards. | ||
|
||
Internally, MQTTMatcher use a prefix tree (trie) to store | ||
values associated with filters, and has an iter_match() | ||
method to iterate efficiently over all filters that match | ||
some topic name. | ||
""" | ||
|
||
# pylint: disable=too-few-public-methods | ||
class Node: | ||
"""Individual node on the MQTT prefix tree. | ||
""" | ||
|
||
__slots__ = "children", "content" | ||
|
||
def __init__(self): | ||
self.children = {} | ||
self.content = None | ||
|
||
def __init__(self): | ||
self._root = self.Node() | ||
|
||
def __setitem__(self, key, value): | ||
"""Add a topic filter :key to the prefix tree | ||
and associate it to :value""" | ||
node = self._root | ||
for sym in key.split("/"): | ||
node = node.children.setdefault(sym, self.Node()) | ||
node.content = value | ||
|
||
def __getitem__(self, key): | ||
"""Retrieve the value associated with some topic filter :key""" | ||
try: | ||
node = self._root | ||
for sym in key.split("/"): | ||
node = node.children[sym] | ||
if node.content is None: | ||
raise KeyError(key) | ||
return node.content | ||
except KeyError: | ||
raise KeyError(key) | ||
|
||
def __delitem__(self, key): | ||
"""Delete the value associated with some topic filter :key""" | ||
lst = [] | ||
try: | ||
parent, node = None, self._root | ||
for k in key.split("/"): | ||
parent, node = node, node.children[k] | ||
lst.append((parent, k, node)) | ||
node.content = None | ||
except KeyError: | ||
raise KeyError(key) | ||
else: # cleanup | ||
for parent, k, node in reversed(lst): | ||
if node.children or node.content is not None: | ||
break | ||
del parent.children[k] | ||
|
||
def iter_match(self, topic): | ||
"""Return an iterator on all values associated with filters | ||
that match the :topic""" | ||
lst = topic.split("/") | ||
normal = not topic.startswith("$") | ||
|
||
def rec(node, i=0): | ||
if i == len(lst): | ||
if node.content is not None: | ||
yield node.content | ||
else: | ||
part = lst[i] | ||
if part in node.children: | ||
for content in rec(node.children[part], i + 1): | ||
yield content | ||
if "+" in node.children and (normal or i > 0): | ||
for content in rec(node.children["+"], i + 1): | ||
yield content | ||
if "#" in node.children and (normal or i > 0): | ||
content = node.children["#"].content | ||
if content is not None: | ||
yield content | ||
|
||
return rec(self._root) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import time | ||
import board | ||
import busio | ||
from digitalio import DigitalInOut | ||
import neopixel | ||
from adafruit_esp32spi import adafruit_esp32spi | ||
from adafruit_esp32spi import adafruit_esp32spi_wifimanager | ||
import adafruit_esp32spi.adafruit_esp32spi_socket as socket | ||
import adafruit_minimqtt.adafruit_minimqtt as MQTT | ||
|
||
### WiFi ### | ||
|
||
# Get wifi details and more from a secrets.py file | ||
try: | ||
from secrets import secrets | ||
except ImportError: | ||
print("WiFi secrets are kept in secrets.py, please add them there!") | ||
raise | ||
|
||
# If you are using a board with pre-defined ESP32 Pins: | ||
esp32_cs = DigitalInOut(board.ESP_CS) | ||
esp32_ready = DigitalInOut(board.ESP_BUSY) | ||
esp32_reset = DigitalInOut(board.ESP_RESET) | ||
|
||
# If you have an externally connected ESP32: | ||
# esp32_cs = DigitalInOut(board.D9) | ||
# esp32_ready = DigitalInOut(board.D10) | ||
# esp32_reset = DigitalInOut(board.D5) | ||
|
||
spi = busio.SPI(board.SCK, board.MOSI, board.MISO) | ||
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) | ||
"""Use below for Most Boards""" | ||
status_light = neopixel.NeoPixel( | ||
board.NEOPIXEL, 1, brightness=0.2 | ||
) # Uncomment for Most Boards | ||
"""Uncomment below for ItsyBitsy M4""" | ||
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) | ||
# Uncomment below for an externally defined RGB LED | ||
# import adafruit_rgbled | ||
# from adafruit_esp32spi import PWMOut | ||
# RED_LED = PWMOut.PWMOut(esp, 26) | ||
# GREEN_LED = PWMOut.PWMOut(esp, 27) | ||
# BLUE_LED = PWMOut.PWMOut(esp, 25) | ||
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) | ||
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) | ||
|
||
### Code ### | ||
|
||
# Define callback methods which are called when events occur | ||
# pylint: disable=unused-argument, redefined-outer-name | ||
def connected(client, userdata, flags, rc): | ||
# This function will be called when the client is connected | ||
# successfully to the broker. | ||
print("Connected to MQTT Broker!") | ||
|
||
|
||
def disconnected(client, userdata, rc): | ||
# This method is called when the client is disconnected | ||
print("Disconnected from MQTT Broker!") | ||
|
||
|
||
def subscribe(client, userdata, topic, granted_qos): | ||
# This method is called when the client subscribes to a new feed. | ||
print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos)) | ||
|
||
|
||
def unsubscribe(client, userdata, topic, pid): | ||
# This method is called when the client unsubscribes from a feed. | ||
print("Unsubscribed from {0} with PID {1}".format(topic, pid)) | ||
|
||
|
||
def on_battery_msg(client, topic, message): | ||
# Method called when device/batteryLife has a new value | ||
print("Battery level: {}v".format(message)) | ||
|
||
# client.remove_topic_callback("device/batteryLevel") | ||
|
||
|
||
def on_message(client, topic, message): | ||
# Method callled when a client's subscribed feed has a new value. | ||
print("New message on topic {0}: {1}".format(topic, message)) | ||
|
||
|
||
# Connect to WiFi | ||
print("Connecting to WiFi...") | ||
wifi.connect() | ||
print("Connected!") | ||
|
||
MQTT.set_socket(socket, esp) | ||
|
||
# Set up a MiniMQTT Client | ||
client = MQTT.MQTT(broker=secrets["broker"], port=secrets["broker_port"]) | ||
|
||
# Setup the callback methods above | ||
client.on_connect = connected | ||
client.on_disconnect = disconnected | ||
client.on_subscribe = subscribe | ||
client.on_unsubscribe = unsubscribe | ||
client.on_message = on_message | ||
client.add_topic_callback("device/batteryLevel", on_battery_msg) | ||
|
||
# Connect the client to the MQTT broker. | ||
print("Connecting to MQTT broker...") | ||
client.connect() | ||
|
||
# Subscribe to all notifications on the device/ topic | ||
client.subscribe("device/#", 1) | ||
|
||
# Start a blocking message loop... | ||
# NOTE: NO code below this loop will execute | ||
while True: | ||
try: | ||
client.loop() | ||
except (ValueError, RuntimeError) as e: | ||
print("Failed to get data, retrying\n", e) | ||
wifi.reset() | ||
client.reconnect() | ||
continue | ||
time.sleep(1) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.