Added the MQTT protocol version to the list of connection parameters. eric7

Sun, 18 Jul 2021 18:30:15 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 18 Jul 2021 18:30:15 +0200
branch
eric7
changeset 97
21f9c010dc42
parent 96
06d1cba236eb
child 98
85d56e77e9df

Added the MQTT protocol version to the list of connection parameters.

MqttMonitor/MqttClient.py file | annotate | diff | comparison | revisions
MqttMonitor/MqttConnectionOptionsDialog.py file | annotate | diff | comparison | revisions
MqttMonitor/MqttConnectionOptionsDialog.ui file | annotate | diff | comparison | revisions
MqttMonitor/MqttConnectionProfilesDialog.py file | annotate | diff | comparison | revisions
MqttMonitor/MqttConnectionProfilesDialog.ui file | annotate | diff | comparison | revisions
MqttMonitor/MqttMonitorWidget.py file | annotate | diff | comparison | revisions
--- a/MqttMonitor/MqttClient.py	Mon Jun 28 17:44:07 2021 +0200
+++ b/MqttMonitor/MqttClient.py	Sun Jul 18 18:30:15 2021 +0200
@@ -7,6 +7,8 @@
 Module implementing a PyQt wrapper around the paho MQTT client.
 """
 
+import enum
+
 from PyQt6.QtCore import (
     pyqtSignal, pyqtSlot, QObject, QCoreApplication, QTimer
 )
@@ -16,6 +18,15 @@
 from Utilities.crypto import pwConvert
 
 
+class MqttProtocols(enum.IntEnum):
+    """
+    Class defining the supported MQTT protocol versions.
+    """
+    MQTTv31 = mqtt.MQTTv31
+    MQTTv311 = mqtt.MQTTv311
+    MQTTv5 = mqtt.MQTTv5
+
+
 class MqttClient(QObject):
     """
     Class implementing a PyQt wrapper around the paho MQTT client.
@@ -73,7 +84,7 @@
         @param userdata user data
         @type any
         @param protocol version of the MQTT protocol to use
-        @type int, one of mqtt.MQTTv3, mqtt.MQTTv311 or mqtt.MQTTv5
+        @type int, one of mqtt.MQTTv31, mqtt.MQTTv311 or mqtt.MQTTv5
         @param transport transport to be used
         @type str, one of "tcp" or "websockets"
         @param parent reference to the parent object
@@ -91,10 +102,13 @@
         
         self.onConnect.connect(self.__connectTimeoutTimer.stop)
         
-        # TODO: MQTTv5: set clean_session to None and remember cleanSession
+        self.__cleanSession = cleanSession
+        if protocol == MqttProtocols.MQTTv5:
+            cleanSession = None
+        
         self.__mqttClient = mqtt.Client(
-            client_id=clientId, clean_session=cleanSession, userdata=None,
-            protocol=mqtt.MQTTv311, transport="tcp")
+            client_id=clientId, clean_session=cleanSession, userdata=userdata,
+            protocol=int(protocol), transport=transport)
         
         self.__initCallbacks()
 
@@ -371,20 +385,22 @@
             self.connectToServer(host, port=port, keepalive=keepalive,
                                  bindAddress=bindAddress)
     
-    def defaultConnectionOptions(self):
+    @classmethod
+    def defaultConnectionOptions(cls):
         """
-        Public method to get a connection options dictionary with default
+        Class method to get a connection options dictionary with default
         values.
         
         @return dictionary containing the default connection options. It has
-            the keys "ClientId", "Keepalive", "CleanSession", "Username",
-            "Password", "WillTopic", "WillMessage", "WillQos", "WillRetain",
-            "TlsEnable", "TlsCaCert", "TlsClientCert", "TlsClientKey",
-            "ConnectionTimeout".
+            the keys "ClientId", "Protocol", "ConnectionTimeout", "Keepalive",
+            "CleanSession", "Username", "Password", "WillTopic", "WillMessage",
+            "WillQos", "WillRetain", "TlsEnable", "TlsCaCert", "TlsClientCert",
+            "TlsClientKey".
         @rtype dict
         """
         return {
             "ClientId": "ERIC7_MQTT_MONITOR_CLIENT",
+            "Protocol": MqttProtocols.MQTTv311,
             "ConnectionTimeout": MqttClient.DefaultConnectTimeout,
             "Keepalive": 60,
             "CleanSession": True,
--- a/MqttMonitor/MqttConnectionOptionsDialog.py	Mon Jun 28 17:44:07 2021 +0200
+++ b/MqttMonitor/MqttConnectionOptionsDialog.py	Sun Jul 18 18:30:15 2021 +0200
@@ -15,6 +15,8 @@
 
 from .Ui_MqttConnectionOptionsDialog import Ui_MqttConnectionOptionsDialog
 
+from .MqttClient import MqttClient, MqttProtocols
+
 from Utilities.crypto import pwConvert
 
 
@@ -22,17 +24,15 @@
     """
     Class implementing a dialog to enter MQTT connection options.
     """
-    def __init__(self, client, options=None, parent=None):
+    def __init__(self, options=None, parent=None):
         """
         Constructor
         
-        @param client reference to the MQTT client object
-        @type MqttClient
         @param options dictionary containing the connection options to
             populate the dialog with. It must have the keys "ClientId",
-            "Keepalive", "CleanSession", "Username", "Password", "WillTopic",
-            "WillMessage", "WillQos", "WillRetain", "TlsEnable", "TlsCaCert",
-            "ConnectionTimeout".
+            "Protocol", "ConnectionTimeout", "Keepalive", "CleanSession",
+            "Username", "Password", "WillTopic", "WillMessage", "WillQos",
+            "WillRetain", "TlsEnable", "TlsCaCert".
         @type dict
         @param parent reference to the parent widget
         @type QWidget
@@ -40,8 +40,6 @@
         super().__init__(parent)
         self.setupUi(self)
         
-        self.__client = client
-        
         self.tlsCertsFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
         self.tlsCertsFilePicker.setFilters(
             self.tr("Certificate Files (*.crt *.pem);;All Files (*)"))
@@ -106,10 +104,16 @@
         @type dict
         """
         if options is None:
-            options = self.__client.defaultConnectionOptions()
+            options = MqttClient.defaultConnectionOptions()
         
         # general
         self.clientIdEdit.setText(options["ClientId"])
+        self.mqttv31Button.setChecked(
+            options["Protocol"] == MqttProtocols.MQTTv31)
+        self.mqttv311Button.setChecked(
+            options["Protocol"] == MqttProtocols.MQTTv311)
+        self.mqttv5Button.setChecked(
+            options["Protocol"] == MqttProtocols.MQTTv5)
         self.connectionTimeoutSpinBox.setValue(options["ConnectionTimeout"])
         self.keepaliveSpinBox.setValue(options["Keepalive"])
         self.cleanSessionCheckBox.setChecked(options["CleanSession"])
@@ -133,13 +137,23 @@
         Public method get the entered connection options.
         
         @return dictionary containing the connection options. It has the keys
-            "ClientId", "Keepalive", "CleanSession", "Username", "Password",
-            "WillTopic", "WillMessage", "WillQos", "WillRetain", "TlsEnable",
-            "TlsCaCert", "ConnectionTimeout".
+            "ClientId", "Protocol", "ConnectionTimeout", "Keepalive",
+            "CleanSession", "Username", "Password", "WillTopic", "WillMessage",
+            "WillQos", "WillRetain", "TlsEnable", "TlsCaCert".
         @rtype dict
         """
+        if self.mqttv31Button.isChecked():
+            protocol = MqttProtocols.MQTTv31
+        elif self.mqttv311Button.isChecked():
+            protocol = MqttProtocols.MQTTv311
+        elif self.mqttv5Button.isChecked():
+            protocol = MqttProtocols.MQTTv5
+        else:
+            protocol = MqttProtocols.MQTTv311
+        
         return {
             "ClientId": self.clientIdEdit.text(),
+            "Protocol": protocol,
             "ConnectionTimeout": self.connectionTimeoutSpinBox.value(),
             "Keepalive": self.keepaliveSpinBox.value(),
             "CleanSession": self.cleanSessionCheckBox.isChecked(),
--- a/MqttMonitor/MqttConnectionOptionsDialog.ui	Mon Jun 28 17:44:07 2021 +0200
+++ b/MqttMonitor/MqttConnectionOptionsDialog.ui	Sun Jul 18 18:30:15 2021 +0200
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>550</width>
-    <height>650</height>
+    <height>675</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -16,50 +16,96 @@
   <property name="sizeGripEnabled">
    <bool>true</bool>
   </property>
-  <layout class="QVBoxLayout" name="verticalLayout">
+  <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
     <widget class="QGroupBox" name="groupBox">
      <property name="title">
       <string>General</string>
      </property>
-     <layout class="QGridLayout" name="gridLayout">
-      <item row="0" column="0">
-       <widget class="QLabel" name="label">
-        <property name="text">
-         <string>Client ID:</string>
-        </property>
-       </widget>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>Client ID:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="clientIdEdit">
+          <property name="toolTip">
+           <string>Enter the ID string for this client</string>
+          </property>
+          <property name="clearButtonEnabled">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="generateIdButton">
+          <property name="toolTip">
+           <string>Press to generate a client ID</string>
+          </property>
+          <property name="text">
+           <string>Generate</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
       </item>
-      <item row="0" column="1">
-       <widget class="QLineEdit" name="clientIdEdit">
-        <property name="toolTip">
-         <string>Enter the ID string for this client</string>
+      <item>
+       <widget class="QGroupBox" name="groupBox_5">
+        <property name="title">
+         <string>MQTT Protocol</string>
         </property>
-        <property name="clearButtonEnabled">
-         <bool>true</bool>
-        </property>
+        <layout class="QHBoxLayout" name="horizontalLayout_3">
+         <item>
+          <widget class="QRadioButton" name="mqttv31Button">
+           <property name="toolTip">
+            <string>Select to use the MQTT 3.1 protocol</string>
+           </property>
+           <property name="text">
+            <string>v 3.1</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="mqttv311Button">
+           <property name="toolTip">
+            <string>Select to use the MQTT 3.1.1 protocol</string>
+           </property>
+           <property name="text">
+            <string>v 3.1.1</string>
+           </property>
+           <property name="checked">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="mqttv5Button">
+           <property name="toolTip">
+            <string>Select to use the MQTT 5.0 protocol</string>
+           </property>
+           <property name="text">
+            <string>v 5.0</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
        </widget>
       </item>
-      <item row="0" column="2">
-       <widget class="QPushButton" name="generateIdButton">
-        <property name="toolTip">
-         <string>Press to generate a client ID</string>
-        </property>
-        <property name="text">
-         <string>Generate</string>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="0">
-       <widget class="QLabel" name="label_6">
-        <property name="text">
-         <string>Connection Timeout:</string>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="1" colspan="2">
-       <layout class="QHBoxLayout" name="horizontalLayout_2">
-        <item>
+      <item>
+       <layout class="QGridLayout" name="gridLayout">
+        <item row="0" column="0">
+         <widget class="QLabel" name="label_6">
+          <property name="text">
+           <string>Connection Timeout:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
          <widget class="QSpinBox" name="connectionTimeoutSpinBox">
           <property name="toolTip">
            <string>Enter the connection timeout in seconds</string>
@@ -81,7 +127,7 @@
           </property>
          </widget>
         </item>
-        <item>
+        <item row="0" column="2">
          <spacer name="horizontalSpacer_2">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
@@ -94,18 +140,14 @@
           </property>
          </spacer>
         </item>
-       </layout>
-      </item>
-      <item row="2" column="0">
-       <widget class="QLabel" name="label_2">
-        <property name="text">
-         <string>Keep Alive Interval:</string>
-        </property>
-       </widget>
-      </item>
-      <item row="2" column="1" colspan="2">
-       <layout class="QHBoxLayout" name="horizontalLayout">
-        <item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="label_2">
+          <property name="text">
+           <string>Keep Alive Interval:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
          <widget class="QSpinBox" name="keepaliveSpinBox">
           <property name="toolTip">
            <string>Enter the keep alive interval in seconds</string>
@@ -127,7 +169,7 @@
           </property>
          </widget>
         </item>
-        <item>
+        <item row="1" column="2">
          <spacer name="horizontalSpacer">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
@@ -142,7 +184,7 @@
         </item>
        </layout>
       </item>
-      <item row="3" column="0" colspan="3">
+      <item>
        <widget class="QCheckBox" name="cleanSessionCheckBox">
         <property name="toolTip">
          <string>Select to start with a clean session</string>
@@ -331,15 +373,18 @@
  <tabstops>
   <tabstop>clientIdEdit</tabstop>
   <tabstop>generateIdButton</tabstop>
+  <tabstop>mqttv31Button</tabstop>
+  <tabstop>mqttv311Button</tabstop>
+  <tabstop>mqttv5Button</tabstop>
   <tabstop>connectionTimeoutSpinBox</tabstop>
   <tabstop>keepaliveSpinBox</tabstop>
   <tabstop>cleanSessionCheckBox</tabstop>
   <tabstop>usernameEdit</tabstop>
   <tabstop>passwordEdit</tabstop>
   <tabstop>willTopicEdit</tabstop>
-  <tabstop>willMessageEdit</tabstop>
   <tabstop>willQosSpinBox</tabstop>
   <tabstop>willRetainCheckBox</tabstop>
+  <tabstop>willMessageEdit</tabstop>
   <tabstop>tlsEnableCheckBox</tabstop>
   <tabstop>tlsCertsFilePicker</tabstop>
  </tabstops>
--- a/MqttMonitor/MqttConnectionProfilesDialog.py	Mon Jun 28 17:44:07 2021 +0200
+++ b/MqttMonitor/MqttConnectionProfilesDialog.py	Sun Jul 18 18:30:15 2021 +0200
@@ -20,6 +20,8 @@
 
 from .Ui_MqttConnectionProfilesDialog import Ui_MqttConnectionProfilesDialog
 
+from .MqttClient import MqttClient, MqttProtocols
+
 import UI.PixmapCache
 from Utilities.crypto import pwConvert
 
@@ -28,18 +30,16 @@
     """
     Class implementing a dialog to edit the MQTT connection profiles.
     """
-    def __init__(self, client, profiles, parent=None):
+    def __init__(self, profiles, parent=None):
         """
         Constructor
         
-        @param client reference to the MQTT client object
-        @type MqttClient
         @param profiles dictionary containing dictionaries containing the
             connection parameters. Each entry must have the keys
-            "BrokerAddress", "BrokerPort", "ClientId",
-            "Keepalive", "CleanSession", "Username", "Password", "WillTopic",
-            "WillMessage", "WillQos", "WillRetain", "TlsEnable", "TlsCaCert",
-            "TlsClientCert", "TlsClientKey", "ConnectionTimeout".
+            "BrokerAddress", "BrokerPort", "ClientId", "Protocol",
+            "ConnectionTimeout", "Keepalive", "CleanSession", "Username",
+            "Password", "WillTopic", "WillMessage", "WillQos", "WillRetain",
+            "TlsEnable", "TlsCaCert", "TlsClientCert", "TlsClientKey".
         @type dict
         @param parent reference to the parent widget
         @type QWidget
@@ -47,8 +47,6 @@
         super().__init__(parent)
         self.setupUi(self)
         
-        self.__client = client
-        
         self.__profiles = collections.defaultdict(self.__defaultProfile)
         self.__profiles.update(profiles)
         self.__profilesChanged = False
@@ -239,10 +237,10 @@
         
         @return dictionary containing dictionaries containing the defined
             connection profiles. Each entry have the keys "BrokerAddress",
-            "BrokerPort", "ClientId", "Keepalive", "CleanSession", "Username",
-            "Password", "WillTopic", "WillMessage", "WillQos", "WillRetain",
-            "TlsEnable", "TlsCaCert", "TlsClientCert", "TlsClientKey",
-            "ConnectionTimeout".
+            "BrokerPort", "ClientId", "Protocol", "ConnectionTimeout",
+            "Keepalive", "CleanSession", "Username", "Password", "WillTopic",
+            "WillMessage", "WillQos", "WillRetain", "TlsEnable", "TlsCaCert",
+            "TlsClientCert", "TlsClientKey".
         @rtype dict
         """
         profilesDict = {}
@@ -256,11 +254,21 @@
         @return name of the applied profile
         @rtype str
         """
+        if self.mqttv31Button.isChecked():
+            protocol = MqttProtocols.MQTTv31
+        elif self.mqttv311Button.isChecked():
+            protocol = MqttProtocols.MQTTv311
+        elif self.mqttv5Button.isChecked():
+            protocol = MqttProtocols.MQTTv5
+        else:
+            protocol = MqttProtocols.MQTTv311
+        
         profileName = self.profileEdit.text()
-        profile = {
+        connectionProfile = {
             "BrokerAddress": self.brokerAddressEdit.text(),
             "BrokerPort": self.brokerPortSpinBox.value(),
             "ClientId": self.clientIdEdit.text(),
+            "Protocol": protocol,
             "ConnectionTimeout": self.connectionTimeoutSpinBox.value(),
             "Keepalive": self.keepaliveSpinBox.value(),
             "CleanSession": self.cleanSessionCheckBox.isChecked(),
@@ -275,18 +283,18 @@
             "TlsClientCert": "",
             "TlsClientKey": "",
         }
-        if profile["TlsEnable"]:
+        if connectionProfile["TlsEnable"]:
             if self.tlsCertsFileButton.isChecked():
-                profile["TlsCaCert"] = self.tlsCertsFilePicker.text()
+                connectionProfile["TlsCaCert"] = self.tlsCertsFilePicker.text()
             elif self.tlsSelfSignedCertsButton.isChecked():
-                profile["TlsCaCert"] = (
+                connectionProfile["TlsCaCert"] = (
                     self.tlsSelfSignedCertsFilePicker.text())
-                profile["TlsClientCert"] = (
+                connectionProfile["TlsClientCert"] = (
                     self.tlsSelfSignedClientCertFilePicker.text())
-                profile["TlsClientKey"] = (
+                connectionProfile["TlsClientKey"] = (
                     self.tlsSelfSignedClientKeyFilePicker.text())
         
-        self.__profiles[profileName] = profile
+        self.__profiles[profileName] = connectionProfile
         self.__profilesChanged = True
         
         return profileName
@@ -298,7 +306,7 @@
         @return default dictionary entry
         @rtype dict
         """
-        defaultProfile = self.__client.defaultConnectionOptions()
+        defaultProfile = MqttClient.defaultConnectionOptions()
         defaultProfile["BrokerAddress"] = ""
         if defaultProfile["TlsEnable"]:
             defaultProfile["BrokerPort"] = 8883
@@ -338,36 +346,48 @@
         @param profileName name of the profile to get data from
         @type str
         """
-        profile = self.__defaultProfile()
+        connectionProfile = self.__defaultProfile()
         if profileName:
-            profile.update(self.__profiles[profileName])
+            connectionProfile.update(self.__profiles[profileName])
         
         self.__populatingProfile = True
         if profileName is not None:
             self.profileEdit.setText(profileName)
-        self.brokerAddressEdit.setText(profile["BrokerAddress"])
-        self.brokerPortSpinBox.setValue(profile["BrokerPort"])
-        self.clientIdEdit.setText(profile["ClientId"])
-        self.connectionTimeoutSpinBox.setValue(profile["ConnectionTimeout"])
-        self.keepaliveSpinBox.setValue(profile["Keepalive"])
-        self.cleanSessionCheckBox.setChecked(profile["CleanSession"])
-        self.usernameEdit.setText(profile["Username"])
-        self.passwordEdit.setText(pwConvert(profile["Password"], encode=False))
-        self.willTopicEdit.setText(profile["WillTopic"])
-        self.willMessageEdit.setPlainText(profile["WillMessage"])
-        self.willQosSpinBox.setValue(profile["WillQos"])
-        self.willRetainCheckBox.setChecked(profile["WillRetain"])
-        self.tlsGroupBox.setChecked(profile["TlsEnable"])
-        if profile["TlsCaCert"] and profile["TlsClientCert"]:
+        self.brokerAddressEdit.setText(connectionProfile["BrokerAddress"])
+        self.brokerPortSpinBox.setValue(connectionProfile["BrokerPort"])
+        self.clientIdEdit.setText(connectionProfile["ClientId"])
+        self.mqttv31Button.setChecked(
+            connectionProfile["Protocol"] == MqttProtocols.MQTTv31)
+        self.mqttv311Button.setChecked(
+            connectionProfile["Protocol"] == MqttProtocols.MQTTv311)
+        self.mqttv5Button.setChecked(
+            connectionProfile["Protocol"] == MqttProtocols.MQTTv5)
+        self.connectionTimeoutSpinBox.setValue(
+            connectionProfile["ConnectionTimeout"])
+        self.keepaliveSpinBox.setValue(connectionProfile["Keepalive"])
+        self.cleanSessionCheckBox.setChecked(connectionProfile["CleanSession"])
+        self.usernameEdit.setText(connectionProfile["Username"])
+        self.passwordEdit.setText(
+            pwConvert(connectionProfile["Password"], encode=False))
+        self.willTopicEdit.setText(connectionProfile["WillTopic"])
+        self.willMessageEdit.setPlainText(connectionProfile["WillMessage"])
+        self.willQosSpinBox.setValue(connectionProfile["WillQos"])
+        self.willRetainCheckBox.setChecked(connectionProfile["WillRetain"])
+        self.tlsGroupBox.setChecked(connectionProfile["TlsEnable"])
+        if (
+            connectionProfile["TlsCaCert"] and
+            connectionProfile["TlsClientCert"]
+        ):
             self.tlsSelfSignedCertsButton.setChecked(True)
-            self.tlsSelfSignedCertsFilePicker.setText(profile["TlsCaCert"])
+            self.tlsSelfSignedCertsFilePicker.setText(
+                connectionProfile["TlsCaCert"])
             self.tlsSelfSignedClientCertFilePicker.setText(
-                profile["TlsClientCert"])
+                connectionProfile["TlsClientCert"])
             self.tlsSelfSignedClientKeyFilePicker.setText(
-                profile["TlsClientKey"])
-        elif profile["TlsCaCert"]:
+                connectionProfile["TlsClientKey"])
+        elif connectionProfile["TlsCaCert"]:
             self.tlsCertsFileButton.setChecked(True)
-            self.tlsCertsFilePicker.setText(profile["TlsCaCert"])
+            self.tlsCertsFilePicker.setText(connectionProfile["TlsCaCert"])
         else:
             self.tlsDefaultCertsButton.setChecked(True)
         self.__populatingProfile = False
@@ -385,6 +405,7 @@
         self.brokerAddressEdit.setText("")
         self.brokerPortSpinBox.setValue(1883)
         self.clientIdEdit.setText("")
+        self.mqttv311Button.setChecked(True)
         self.keepaliveSpinBox.setValue(60)
         self.cleanSessionCheckBox.setChecked(True)
         self.usernameEdit.setText("")
@@ -436,40 +457,56 @@
             return False
         
         elif profileName in self.__profiles:
-            profile = self.__defaultProfile()
-            profile.update(self.__profiles[profileName])
+            if self.mqttv31Button.isChecked():
+                protocol = MqttProtocols.MQTTv31
+            elif self.mqttv311Button.isChecked():
+                protocol = MqttProtocols.MQTTv311
+            elif self.mqttv5Button.isChecked():
+                protocol = MqttProtocols.MQTTv5
+            else:
+                protocol = MqttProtocols.MQTTv311
+            
+            connectionProfile = self.__defaultProfile()
+            connectionProfile.update(self.__profiles[profileName])
             changed = (
-                self.brokerAddressEdit.text() != profile["BrokerAddress"] or
-                self.brokerPortSpinBox.value() != profile["BrokerPort"] or
-                self.clientIdEdit.text() != profile["ClientId"] or
+                self.brokerAddressEdit.text() !=
+                connectionProfile["BrokerAddress"] or
+                self.brokerPortSpinBox.value() !=
+                connectionProfile["BrokerPort"] or
+                self.clientIdEdit.text() != connectionProfile["ClientId"] or
+                protocol != connectionProfile["Protocol"] or
                 self.connectionTimeoutSpinBox.value() !=
-                profile["ConnectionTimeout"] or
-                self.keepaliveSpinBox.value() != profile["Keepalive"] or
+                connectionProfile["ConnectionTimeout"] or
+                self.keepaliveSpinBox.value() !=
+                connectionProfile["Keepalive"] or
                 self.cleanSessionCheckBox.isChecked() !=
-                profile["CleanSession"] or
-                self.usernameEdit.text() != profile["Username"] or
+                connectionProfile["CleanSession"] or
+                self.usernameEdit.text() != connectionProfile["Username"] or
                 self.passwordEdit.text() !=
-                pwConvert(profile["Password"], encode=False) or
-                self.willTopicEdit.text() != profile["WillTopic"] or
-                self.willMessageEdit.toPlainText() != profile["WillMessage"] or
-                self.willQosSpinBox.value() != profile["WillQos"] or
-                self.willRetainCheckBox.isChecked() != profile["WillRetain"] or
-                self.tlsGroupBox.isChecked() != profile["TlsEnable"]
+                pwConvert(connectionProfile["Password"], encode=False) or
+                self.willTopicEdit.text() != connectionProfile["WillTopic"] or
+                self.willMessageEdit.toPlainText() !=
+                connectionProfile["WillMessage"] or
+                self.willQosSpinBox.value() != connectionProfile["WillQos"] or
+                self.willRetainCheckBox.isChecked() !=
+                connectionProfile["WillRetain"] or
+                self.tlsGroupBox.isChecked() != connectionProfile["TlsEnable"]
             )
             # check TLS stuff only, if not yet changed
             if not changed:
                 if self.tlsCertsFileButton.isChecked():
                     changed |= (
-                        self.tlsCertsFilePicker.text() != profile["TlsCaCert"]
+                        self.tlsCertsFilePicker.text() !=
+                        connectionProfile["TlsCaCert"]
                     )
                 elif self.tlsSelfSignedCertsButton.isChecked():
                     changed |= (
                         self.tlsSelfSignedCertsFilePicker.text() !=
-                        profile["TlsCaCert"] or
+                        connectionProfile["TlsCaCert"] or
                         self.tlsSelfSignedClientCertFilePicker.text() !=
-                        profile["TlsClientCert"] or
+                        connectionProfile["TlsClientCert"] or
                         self.tlsSelfSignedClientKeyFilePicker.text() !=
-                        profile["TlsClientKey"]
+                        connectionProfile["TlsClientKey"]
                     )
             return changed
         
--- a/MqttMonitor/MqttConnectionProfilesDialog.ui	Mon Jun 28 17:44:07 2021 +0200
+++ b/MqttMonitor/MqttConnectionProfilesDialog.ui	Sun Jul 18 18:30:15 2021 +0200
@@ -224,14 +224,56 @@
            <string>General</string>
           </attribute>
           <layout class="QGridLayout" name="gridLayout_2">
-           <item row="0" column="0">
+           <item row="0" column="0" colspan="2">
+            <widget class="QGroupBox" name="groupBox_5">
+             <property name="title">
+              <string>MQTT Protocol</string>
+             </property>
+             <layout class="QHBoxLayout" name="horizontalLayout_7">
+              <item>
+               <widget class="QRadioButton" name="mqttv31Button">
+                <property name="toolTip">
+                 <string>Select to use the MQTT 3.1 protocol</string>
+                </property>
+                <property name="text">
+                 <string>v 3.1</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QRadioButton" name="mqttv311Button">
+                <property name="toolTip">
+                 <string>Select to use the MQTT 3.1.1 protocol</string>
+                </property>
+                <property name="text">
+                 <string>v 3.1.1</string>
+                </property>
+                <property name="checked">
+                 <bool>true</bool>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QRadioButton" name="mqttv5Button">
+                <property name="toolTip">
+                 <string>Select to use the MQTT 5.0 protocol</string>
+                </property>
+                <property name="text">
+                 <string>v 5.0</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </item>
+           <item row="1" column="0">
             <widget class="QLabel" name="label_12">
              <property name="text">
               <string>Connection Timeout:</string>
              </property>
             </widget>
            </item>
-           <item row="0" column="1">
+           <item row="1" column="1">
             <layout class="QHBoxLayout" name="horizontalLayout_5">
              <item>
               <widget class="QSpinBox" name="connectionTimeoutSpinBox">
@@ -270,14 +312,14 @@
              </item>
             </layout>
            </item>
-           <item row="1" column="0">
+           <item row="2" column="0">
             <widget class="QLabel" name="label_5">
              <property name="text">
               <string>Keep Alive Interval:</string>
              </property>
             </widget>
            </item>
-           <item row="1" column="1">
+           <item row="2" column="1">
             <layout class="QHBoxLayout" name="horizontalLayout_4">
              <item>
               <widget class="QSpinBox" name="keepaliveSpinBox">
@@ -316,7 +358,7 @@
              </item>
             </layout>
            </item>
-           <item row="2" column="0" colspan="2">
+           <item row="3" column="0" colspan="2">
             <widget class="QCheckBox" name="cleanSessionCheckBox">
              <property name="toolTip">
               <string>Select to start with a clean session</string>
@@ -326,7 +368,7 @@
              </property>
             </widget>
            </item>
-           <item row="3" column="1">
+           <item row="4" column="1">
             <spacer name="verticalSpacer">
              <property name="orientation">
               <enum>Qt::Vertical</enum>
@@ -742,6 +784,9 @@
   <tabstop>clientIdEdit</tabstop>
   <tabstop>generateIdButton</tabstop>
   <tabstop>profileTabWidget</tabstop>
+  <tabstop>mqttv31Button</tabstop>
+  <tabstop>mqttv311Button</tabstop>
+  <tabstop>mqttv5Button</tabstop>
   <tabstop>connectionTimeoutSpinBox</tabstop>
   <tabstop>keepaliveSpinBox</tabstop>
   <tabstop>cleanSessionCheckBox</tabstop>
@@ -749,9 +794,9 @@
   <tabstop>passwordEdit</tabstop>
   <tabstop>showPasswordButton</tabstop>
   <tabstop>willTopicEdit</tabstop>
-  <tabstop>willMessageEdit</tabstop>
   <tabstop>willQosSpinBox</tabstop>
   <tabstop>willRetainCheckBox</tabstop>
+  <tabstop>willMessageEdit</tabstop>
   <tabstop>tlsGroupBox</tabstop>
   <tabstop>tlsDefaultCertsButton</tabstop>
   <tabstop>tlsCertsFileButton</tabstop>
--- a/MqttMonitor/MqttMonitorWidget.py	Mon Jun 28 17:44:07 2021 +0200
+++ b/MqttMonitor/MqttMonitorWidget.py	Sun Jul 18 18:30:15 2021 +0200
@@ -22,7 +22,8 @@
 from .Ui_MqttMonitorWidget import Ui_MqttMonitorWidget
 
 from .MqttClient import (
-    MqttClient, mqttConnackMessage, mqttErrorMessage, mqttLogLevelString
+    MqttClient, MqttProtocols, mqttConnackMessage, mqttErrorMessage,
+    mqttLogLevelString
 )
 
 import UI.PixmapCache
@@ -184,19 +185,35 @@
         
         self.__statusLoadValues = collections.defaultdict(
             self.__loadDefaultDictFactory)
+    
+    #######################################################################
+    ## Slots handling MQTT related signals
+    #######################################################################
+    
+    def __createClient(self, protocol=MqttProtocols.MQTTv311):
+        """
+        Private method to instantiate a MQTT client for a given protocol.
         
-        self.__client = MqttClient()
+        @param protocol MQTT protocol version to be used (defaults to
+            MqttProtocols.MQTTv311)
+        @type MqttProtocols (optional)
+        @return created and connected MQTT client object
+        @rtype MqttClient
+        """
+        client = MqttClient(protocol=protocol)
         
         # connect the MQTT client signals
-        self.__client.onConnect.connect(self.__brokerConnected)
-        self.__client.onDisconnected.connect(self.__brokerDisconnected)
-        self.__client.onLog.connect(self.__clientLog)
-        self.__client.onMessage.connect(self.__messageReceived)
-        self.__client.onPublish.connect(self.__messagePublished)
-        self.__client.onSubscribe.connect(self.__topicSubscribed)
-        self.__client.onUnsubscribe.connect(self.__topicUnsubscribed)
+        client.onConnect.connect(self.__brokerConnected)
+        client.onDisconnected.connect(self.__brokerDisconnected)
+        client.onLog.connect(self.__clientLog)
+        client.onMessage.connect(self.__messageReceived)
+        client.onPublish.connect(self.__messagePublished)
+        client.onSubscribe.connect(self.__topicSubscribed)
+        client.onUnsubscribe.connect(self.__topicUnsubscribed)
         
-        self.__client.connectTimeout.connect(self.__connectTimeout)
+        client.connectTimeout.connect(self.__connectTimeout)
+        
+        return client
     
     #######################################################################
     ## Slots handling MQTT related signals
@@ -446,8 +463,7 @@
                 MqttConnectionProfilesDialog
             )
             dlg = MqttConnectionProfilesDialog(
-                self.__client, self.__plugin.getPreferences("BrokerProfiles"),
-                parent=self)
+                self.__plugin.getPreferences("BrokerProfiles"), parent=self)
             if dlg.exec() == QDialog.DialogCode.Accepted:
                 profilesDict = dlg.getProfiles()
                 self.__plugin.setPreferences("BrokerProfiles", profilesDict)
@@ -457,7 +473,7 @@
                 MqttConnectionOptionsDialog
             )
             dlg = MqttConnectionOptionsDialog(
-                self.__client, self.__connectionOptions, parent=self)
+                self.__connectionOptions, parent=self)
             if dlg.exec() == QDialog.DialogCode.Accepted:
                 self.__connectionOptions = dlg.getConnectionOptions()
                 if self.__connectionOptions["TlsEnable"]:
@@ -1026,8 +1042,11 @@
             self.__addBrokerToRecent(host, port)
             self.connectButton.setEnabled(False)
             if self.__connectionOptions is None:
+                self.__client = self.__createClient()
                 self.__client.connectToServer(host, port=port)
             else:
+                self.__client = self.__createClient(
+                    protocol=self.__connectionOptions["Protocol"])
                 self.__client.connectToServerWithOptions(
                     host, port=port, options=self.__connectionOptions)
     
@@ -1040,9 +1059,13 @@
             self.__plugin.setPreferences("MostRecentProfile", profileName)
             
             profilesDict = self.__plugin.getPreferences("BrokerProfiles")
-            profile = copy.copy(profilesDict[profileName])      # play it save
-            host = profile["BrokerAddress"]
-            port = profile["BrokerPort"]
+            connectionProfile = copy.copy(profilesDict[profileName])
+            host = connectionProfile["BrokerAddress"]
+            port = connectionProfile["BrokerPort"]
+            try:
+                protocol = connectionProfile["Protocol"]
+            except KeyError:
+                protocol = MqttProtocols.MQTTv311
             
             self.brokerStatusLabel.setText(
                 self.tr("Connecting to {0}:{1} ...").format(
@@ -1050,5 +1073,7 @@
             self.brokerStatusLabel.show()
             
             self.connectButton.setEnabled(False)
-            self.__client.connectToServerWithOptions(host, port=port,
-                                                     options=profile)
+            
+            self.__client = self.__createClient(protocol=protocol)
+            self.__client.connectToServerWithOptions(
+                host, port=port, options=connectionProfile)

eric ide

mercurial