paho.mqtt.python icon indicating copy to clipboard operation
paho.mqtt.python copied to clipboard

Shared Subscription does not match topic using topic_matches_sub()

Open dmarsh19 opened this issue 4 years ago • 3 comments

Using release 1.5.0

I would expect the behavior of this call to return True. topic_matches_sub("$share/group/+/+/c", "a/b/c") The MQTTMessage.topic received from the on_message callback will be the second part (a/b/c) and is a topic that should fall within the shared subscription topicFilter.

I believe this is causing issues with Client.message_callback_add(). If the subscription is a shared subscription (E.g. Client.message_callback_add("$share/group/+/+/c", callback) the callback is never executed.

dmarsh19 avatar Mar 10 '20 20:03 dmarsh19

I can confirm the same problem in the latest release 1.5.1.

For now my workaround is delegating the message to the topic-filter-specific callback within the on_message callback that receives the unmatched messages, but it would be great to see this issue addressed in the next release.

maciejkorzepa avatar Sep 07 '21 15:09 maciejkorzepa

Another quick workaround: it seems you can drop the $share/group portion of the topic filter when adding a message callback.

Quick (runnable) example:

import paho.mqtt.client as mqtt

def handle_yo(client, userdata, msg):
    print(f"Yo: {msg.payload}")

def handle_hi(client, userdata, msg):
    print(f"Hi: {msg.payload}")

def on_connect(client, userdata, flags, rc):
    # The subscription topic is a shared topic
    client.subscribe("$share/group/base/#")
    print("Subscribed")

c = mqtt.Client()
c.on_connect = on_connect

# Message callbacks drop the `$share/group` portion
c.message_callback_add("base/+/yo", handle_yo)
c.message_callback_add("base/+/hi", handle_hi)
c.connect("localhost", 1883)

c.loop_forever()

Running the script above and publishing the following two messages:

mosquitto_pub -t base/a/yo -m "test"
mosquitto_pub -t base/a/hi -m "test"

gives

Subscribed
Yo: b'test'
Hi: b'test'

A generic way to add these callbacks:

def add_message_callback(client, topic, callback):
    """Add a message callback to a paho MQTT client.

    Internally, paho uses a function called `topic_matches_sub` to determine if
    a given topic string matches an MQTT topic pattern. Currently, this function
    does not match shared topics correctly, e.g.,:
        >>> topic_matches_sub("$share/group/+/+/c", "a/b/c")  # expected: True
        False

    Issue link: https://github.com/eclipse/paho.mqtt.python/issues/472

    This function handles stripping the `$share/group` portion of a topic
    (if required) before calling `client.message_callback_add`.
    """
    if topic.startswith("$share"):
        # split the topic into at most 3 items: if this is a shared topic
        # string, it will be of the form:
        #   $share/group_id/rest/of/topic/string
        # this split gives:
        #   ["$share", "group_id", "rest/of/topic/string"]
        # and we only use the last bit (rest/of/topic/string)
        topic = topic.split("/", maxsplit=2)[-1]
    client.message_callback_add(topic, callback)

tomasgareau avatar Sep 23 '21 22:09 tomasgareau

I'm going to flag this as an enhancement. $share was not part of the v3 spec; as it's included in v5 we should really support it here or, at a minimum, document the fact that its not supported.

MattBrittan avatar Jan 08 '24 00:01 MattBrittan