socket error when connecting paho.mqtt.python to mosquitto broker - python

I'm trying to connect from python script(paho.mqtt.python) to mosquitto broker.
I can connect from terminal using this command:
mosquitto_sub -h localhost -p 8883 -v -t 'owntracks/#' -u owntracks -P 12qwaszx
But when I'm trying to connect via python script I'm getting the error:
Socket error on client <unknown>, disconnecting.
The script I'm using is the example:
(from here: https://owntracks.org/booklet/tech/program/)
import paho.mqtt.client as mqtt
import json
# The callback for when the client successfully connects to the broker
def on_connect(client, userdata, rc):
''' We subscribe on_connect() so that if we lose the connection
and reconnect, subscriptions will be renewed. '''
client.subscribe("owntracks/+/+")
#tried also: client.subscribe("owntracks/#")
# The callback for when a PUBLISH message is received from the broker
def on_message(client, userdata, msg):
topic = msg.topic
try:
data = json.loads(str(msg.payload))
print "TID = {0} is currently at {1}, {2}".format(data['tid'], data['lat'], data['lon'])
except:
print "Cannot decode data on topic {0}".format(topic)
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 8883, 60)
# Blocking call which processes all network traffic and dispatches
# callbacks (see on_*() above). It also handles reconnecting.
client.loop_forever()
Here is the content of my config file (I changed the "localhost" from my real IP - tried both of them):
# Place your local configuration in /etc/mosquitto/conf.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example
pid_file /var/run/mosquitto.pid
log_dest file /var/log/mosquitto/mosquitto.log
include_dir /etc/mosquitto/conf.d
listener 8883 "localhost"
persistence true
persistence_location /var/lib/mosquitto/
persistence_file mosquitto.db
log_dest syslog
log_dest stdout
log_dest topic
log_type error
log_type warning
log_type notice
log_type information
connection_messages true
log_timestamp true
allow_anonymous false
password_file /etc/mosquitto/pwfile
Any help would be highly appreciated.

Your python script is attempting to connect to what looks like a TLS secured setup without preparing the client connect method to apply these details to the transaction. Try the following:
def ssl_prep():
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(cafile=ca)
ssl_context.load_cert_chain(certfile=mycert, keyfile=priv)
return ssl_context
ca = "PATH_TO_YOUR_CA_FILE"
priv = "PATH_TO_YOUR_PEM_FILE"
mycert = "PATH_TO_YOUR_CERT_FILE"
topics = "YOUR_TOPICS"
broker = "BROKER_URL"
client = mqtt.Client()
ssl_context= ssl_prep()
client.tls_set_context(context=ssl_context)
client.username_pw_set(username="UNAME",password="PASS")
client.connect(broker, port=8883)
By providing the ssl context to the connection attempt before the attempt is made, this should connect assuming you have all the detail specific to your own setup in place.

Try to reduce the KeepAlive time from 1min to 30sec or lower:
client.connect("localhost", 8883, 30)
//Default: connect(host, port=1883, keepalive=60, bind_address=””)

Related

Mqtt Subscriber, loop_forever() does not work

I am having an issue with the code below.
The code works perfectly at the beginning. First it says Connected to MQTT Broker! and receives data from it. But after a long time (like 6 hours, or 10 hours etc.) it says again Connected to MQTT Broker! and after that id does not receive any other data.
I am trying to make this program work forever, but i don't know what i have done wrong.
Any ideas?
# python3.6
import random
import mysql.connector
from paho.mqtt import client as mqtt_client
import json
# Code for MQTT Connection
broker = 'YOUR_BROKER'
port = 1883
topic = "YOUR_TOPIC"
# generate client ID with pub prefix randomly
client_id = f'python-mqtt-{random.randint(0, 100)}'
username = "THE_USERNAME"
password = "THE_PASSWORD"
# Function to connect on mqtt
def connect_mqtt() -> mqtt_client:
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)
client = mqtt_client.Client(client_id)
client.username_pw_set(username, password)
client.on_connect = on_connect
client.connect(broker, port)
return client
# function to subscribe from mqtt
def subscribeFunc(client: mqtt_client):
def on_messageFunc(client, userdata, msg):
print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
client.subscribe(topic)
client.on_message = on_messageFunc
def run():
client = connect_mqtt()
subscribeFunc(client)
client.loop_forever()
if __name__ == '__main__':
run()
I tried to find the problem but it seems that nothing changed significantly.
I am expecting this program to receive data without stopping.
Network connections may not be 100% reliable (and servers etc are restarted from time to time) so expecting the connection to remain up forever is unrealistic. The issue with your code is that it connects then subscribes; if the connection is dropped it does not resubscribe. As the connection is clean_session=true and subscription qos=0 (the defaults) the broker will forget about any active subscriptions when the connection drops (so the client will reconnect but not receive any more messages).
The Simple solution is to use the approach shown in the docs and subscribe in the on_connect callback (that way the subscription will be renewed after reconnection):
def on_connect(client, userdata, flags, rc):
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("$SYS/#")
client = mqtt.Client()
client.on_connect = on_connect
client.connect("mqtt.eclipseprojects.io", 1883, 60)
You may also want to consider the advice in this answer (as per the comment from #mnikley) because that way the broker will queue up messages for you while the connection is down (otherwise these will be lost).

"can't register client due to register_subscriber_retry_exhausted " when renewing a connection to VerneMQ

We have a Flask app that needs to send messages to different subscriptions on a VernqMQ broker. We are using paho.mqtt library to connect to the broker, and we are using JWT-based authentication, which is validated ok the broker with a custom webhook script. However, the JWT expires every seven days, so the Flask app needs to renew that token and reconnect. We are currently handling this in the on_disconnect event. This setup works, however, after a while, we are starting to get this error message :
can't register client {xxxx} with username 'xxxx' due to register_subscriber_retry_exhausted
The message appears hundreds of times on the broker's log files, but we can't find any information on this error. Here is the code for how we establish/renew the MQTT broker connection within Flask:
def mqtt_on_connect(client, userdata, flags, rc):
if rc == 0:
print("MQTT broker Connection successful")
else:
print("MQTT broker Connection attempt failed.")
def mqtt_on_disconnect(client, userdata, rc):
token = crypto.get_jwt('our_username').decode()
client.username_pw_set("our_username", password=f'Bearer {token}')
try:
client.connect(
app.config['MQTT_BROKER_URL'],
port=app.config['MQTT_BROKER_PORT']
)
except Exception as ex:
print("Could not reconnect to broker.")
print(ex)
def establish_mqtt_connection(app, get_jwt):
token = get_jwt(
'our_username'
).decode()
client_id = str(uuid.uuid4())
if app.config['ENVIRONMENT'] not in {'LOCAL', 'TEST'}:
client_id = str(uuid.uuid4())
mqtt_client = mqtt.Client(
f'control_{client_id}',
clean_session=True,
transport="websockets"
)
mqtt_client.tls_set(
ca_certs=certifi.where(),
tls_version=ssl.PROTOCOL_TLSv1_2
)
mqtt_client.on_connect = mqtt_on_connect
mqtt_client.on_disconnect = mqtt_on_disconnect
mqtt_client.username_pw_set("our_username", password=f'Bearer {token}')
host = app.config['MQTT_BROKER_URL']
mqtt_client._host = host
mqtt_client.loop_start()
try:
mqtt_client.connect(
app.config['MQTT_BROKER_URL'],
port=app.config['MQTT_BROKER_PORT'],
)
except Exception as ex:
print(f'Exception establishing connection to MQTT broker: {ex}')
app.config['MQTT_CLIENT'] = mqtt_client
Why does this error message happen and what can we do to fix it? Is there a problem with how we are setting up the broker connection? Any info on this willbe greatly appreciated.
Additional info: the broker is a single instance of VerneMQ, not a cluster. All configuration values are set to defaults.

paho-mqtt won't give me the queued messages

Using paho-mqtt and trying to have it receive queued messages. The broker I'm using is emqx 4.2.2 and this is my script:
from paho.mqtt.client import Client, MQTTv5
def on_connect(mqttc, obj, flags, rc, other):
print(" Session present: " + str(flags['session present']))
print(" Connection result: " + str(rc))
mqttc.subscribe([
('/message/1', 1)
])
def on_message(*args, **kwargs):
print("received a message")
client = Client(
client_id='test-client-id',
protocol=MQTTv5,
)
client.username_pw_set(
username="test-user-2",
password="test"
)
client.on_connect = on_connect
client.on_message = on_message
client.connect(
host='localhost',
port=1883,
keepalive=60,
clean_start=False,
)
client.loop_forever()
I now go and publish a message to the broker:
mosquitto_pub -u test-user-2 -P test -t '/message/1' -m 'this is a message' -q 1 -V mqttv5
While the client is connected to the broker, It does receive the messages but given that I'm
subscribing with QoS 1 and that messages are published with QoS 1 I am expecting that if I
disconnect my client from the broker, then publish some more QoS 1 messages to that topic and then reconnect my client to the broker using the same fixed client_id, then my client will receive the messages that have been queued while my client was away. Well that's not happening and simulating the same functionality with mosquitto_sub with the -c flag everything works as expected, which leads me to ask myself ... is there a problem with paho-mqtt? Am I doing something wrong?
In MQTT v5, clean start means only whether the session is deleted at the start or not. To control how long the session lasts after you disconnect, you need to set the session expiry interval:
import paho.mqtt.properties as properties
...
connect_properties = properties.Properties(properties.PacketTypes.CONNECT)
connect_properties.SessionExpiryInterval = 3600
client.connect("localhost", 1883, 60, properties=connect_properties)
QOS only works if the client is SUBSCRIBEd...if you want to receive messages before you are Subscribed, you need to PUBLISH messages with the Retain flag set...and then you only get the last message that was sent. If you really need to get multiple messages, then you need to use something like AMQ, and not MQTT.
It should be clean_session not clean_start and it needs to be passed to the Client constructor not the connect() function:
...
client = Client(
client_id='test-client-id',
protocol=MQTTv5,
clean_session=False
)
client.username_pw_set(
username="test-user-2",
password="test"
)
client.on_connect = on_connect
client.on_message = on_message
client.connect(
host='localhost',
port=1883,
keepalive=60
)
...

MQTT Subscriber to ThingsBoard broker in python

Situation:
I have a python virtual sensor (a python program) that submits data via MQTT protocol to my device in ThingsBoard.
I can visualize data on the dashboard, so I'm sure that the data are received.
Problem:
When I try to connect a python subscriber to the thingsboard broker (demo.thingsboard.io) using paho I obtain that the connection code is 0, so the connection is OK, however I see that the dashboard stops visualising the data from the virtual sensor but the subscriber does not receive anything.
The virtual sensor is publishing at v1/devices/me/telemetry and the subscriber is subscribed at the same topic v1/devices/me/telemetry.
How to show the data published by the virtual sensor on my subscriber client?
VIRTUAL SENSOR CODE:
import paho.mqtt.client as paho #mqtt library
import os
import json
import time
from datetime import datetime
ACCESS_TOKEN="vgFztmvT6bps7JCeOEZq" #Token of your device
broker="demo.thingsboard.io" #host name
port=1883 #data listening port
def on_publish(client,userdata,result): #create function for callback
print("data published to thingsboard \n")
pass
client1= paho.Client("control1") #create client object
client1.on_publish = on_publish #assign function to callback
client1.username_pw_set(ACCESS_TOKEN) #access token from thingsboard device
client1.connect(broker,port,keepalive=60) #establish connection
while True:
payload="{"
payload+="\"Humidity\":60,";
payload+="\"Temperature\":25";
payload+="}"
ret= client1.publish("v1/devices/me/telemetry",payload) #topic-v1/devices/me/telemetry
print("Please check LATEST TELEMETRY field of your device")
print(payload);
time.sleep(5)
CLIENT SUBSCRIBER CODE:
import paho.mqtt.client as mqtt
import time
token = "vgFztmvT6bps7JCeOEZq"
broker="demo.thingsboard.io" # host name
port=1883
topic = "v1/devices/me/telemetry"
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc) :
if (rc==0) :
print("connected OK Returned code = ", rc)
else :
print("Bad connection Returned code = ", rc)
def on_message(client, userdata, msg) :
print (msg.topic + " " + str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.username_pw_set(token)
client.connect(broker , port, 60)
client.subscribe(topic)
client.loop_forever()
In you publisher client , you are using a topic named:"v1/devices/me/telemetry".
However, you are not subscribing to the same topic using the subscriber client.
Change the following line in your Subscriber client program:
client.subscribe(token)
to
client.subscribe(topic)
This should solve the problem.
I believe TB will only allow for one connection per client so you should use different access_keys for your publisher and subscriber.
Try to use tcp://51.159.155.114 but not demo.thingsboard.io

Python, MQTT broker, publish message

I have configured MQTT Broker, receiving published messages from another piece of code (not written or accessible by me). I added another topic to the broker configuration and am now trying to publish data to this new topic from a piece of python code. I get the feedback that the message is published by the callback function, but no actual data is received.
Is there anything I am missing?
I am using the following code:
import paho.mqtt.client as mqtt
import time
#=========================================================================
def on_connect(client, userdata, flags, rc) :
print "on_connect()"
#=========================================================================
def on_publish(client, userdata, mid) :
print "on_publish()"
#=========================================================================
def send() :
mqttc = mqtt.Client()
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
#host = "localhost"
host = "127.0.0.1"
port = 1883
keepalive = 60
print "\nConnect to '%s', port '%s', keepalive '%s'" % (host, port, keepalive)
mqttc.connect(host=host, port=port, keepalive=keepalive)
time.sleep(3)
mqttc.loop_start()
time.sleep(3)
topic = "data/MY/TOPIC"
msg = "MY_MESSAGE"
print "Publish to '%s' msg '%s'" % (topic, msg)
mqttc.publish(topic, msg, qos=2)
time.sleep(3)
mqttc.loop_stop()
# end send()
#=========================================================================
if __name__ == "__main__":
send()
# end if
Getting the stdout
Connect to '127.0.0.1', port '1883', keepalive '60'
on_connect()
Publish to 'data/MY/TOPIC' msg 'MY MESSAGE'
on_publish()
I am not sure if the loop() functions are necessary, but if I do not embed the publishing in the loop_start() and loop_stop(), I do not get a on_connect callback.
The loop functions are necessary as these are where all the network traffic is processed.
Manually setting up a connection to the broker to just send a single message like this is not a good idea, it would be better to start the client, leave it running (by calling loop_start() and not calling loop_stop()) in the background and then just call the publish method on the mqttc client object.
If you don't want to keep a instance of the client running then you should use the single message publish helper method provided by the paho python library (https://pypi.python.org/pypi/paho-mqtt/1.1#single):
import paho.mqtt.publish as publish
publish.single("paho/test/single", "payload", hostname="iot.eclipse.org")

Categories