Simple CoAP Client Example

 // Copyright (C) 2019 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

 #include "mainwindow.h"
 #include "optiondialog.h"
 #include "ui_mainwindow.h"

 #include <QCoapResourceDiscoveryReply>
 #include <QCoapReply>
 #include <QDateTime>
 #include <QFileDialog>
 #include <QHostInfo>
 #include <QMessageBox>
 #include <QMetaEnum>
 #include <QNetworkInterface>

 MainWindow::MainWindow(QWidget *parent) :
     QMainWindow(parent),
     ui(new Ui::MainWindow)
 {
     m_client = new QCoapClient(QtCoap::SecurityMode::NoSecurity, this);
     connect(m_client, &QCoapClient::finished, this, &MainWindow::onFinished);
     connect(m_client, &QCoapClient::error, this, &MainWindow::onError);

     ui->setupUi(this);

     ui->methodComboBox->addItem("Get", QVariant::fromValue(QtCoap::Method::Get));
     ui->methodComboBox->addItem("Put", QVariant::fromValue(QtCoap::Method::Put));
     ui->methodComboBox->addItem("Post", QVariant::fromValue(QtCoap::Method::Post));
     ui->methodComboBox->addItem("Delete", QVariant::fromValue(QtCoap::Method::Delete));

     fillHostSelector();
     ui->hostComboBox->setFocus();
 }

 MainWindow::~MainWindow()
 {
     delete ui;
 }

 void MainWindow::fillHostSelector()
 {
     const auto networkInterfaces = QNetworkInterface::allInterfaces();
     for (const auto &interface : networkInterfaces)
         for (const auto &address : interface.addressEntries())
             ui->hostComboBox->addItem(address.ip().toString());
 }

 void MainWindow::addMessage(const QString &message, bool isError)
 {
     const QString content = "--------------- "
             + QDateTime::currentDateTime().toString()
             + " ---------------\n"
             + message + "\n\n";
     ui->textEdit->setTextColor(isError ? Qt::red : Qt::black);
     ui->textEdit->insertPlainText(content);
     ui->textEdit->ensureCursorVisible();
 }

 void MainWindow::onFinished(QCoapReply *reply)
 {
     if (reply->errorReceived() == QtCoap::Error::Ok)
         addMessage(reply->message().payload());
 }

 static QString errorMessage(QtCoap::Error errorCode)
 {
     const auto error = QMetaEnum::fromType<QtCoap::Error>().valueToKey(static_cast<int>(errorCode));
     return QString("Request failed with error: %1\n").arg(error);
 }

 void MainWindow::onError(QCoapReply *reply, QtCoap::Error error)
 {
     const auto errorCode = reply ? reply->errorReceived() : error;
     addMessage(errorMessage(errorCode), true);
 }

 void MainWindow::onDiscovered(QCoapResourceDiscoveryReply *reply, QList<QCoapResource> resources)
 {
     if (reply->errorReceived() != QtCoap::Error::Ok)
         return;

     QString message;
     for (const auto &resource : resources) {
         ui->resourceComboBox->addItem(resource.path());
         message += "Discovered resource: \"" + resource.title() + "\" on path "
                 + resource.path() + "\n";
     }
     addMessage(message);
 }

 void MainWindow::onNotified(QCoapReply *reply, const QCoapMessage &message)
 {
     if (reply->errorReceived() == QtCoap::Error::Ok)
         addMessage("Received observe notification with payload: " + message.payload());
 }

 static QString tryToResolveHostName(const QString hostName)
 {
     const auto hostInfo = QHostInfo::fromName(hostName);
     if (!hostInfo.addresses().empty())
         return hostInfo.addresses().first().toString();

     return hostName;
 }

 void MainWindow::on_runButton_clicked()
 {
     const auto msgType = ui->msgTypeCheckBox->isChecked() ? QCoapMessage::Type::Confirmable
                                                           : QCoapMessage::Type::NonConfirmable;
     QUrl url;
     url.setHost(tryToResolveHostName(ui->hostComboBox->currentText()));
     url.setPort(ui->portSpinBox->value());
     url.setPath(ui->resourceComboBox->currentText());

     QCoapRequest request(url, msgType);
     for (const auto &option : m_options)
         request.addOption(option);
     m_options.clear();

     const auto method = ui->methodComboBox->currentData(Qt::UserRole).value<QtCoap::Method>();
     switch (method) {
     case QtCoap::Method::Get:
         m_client->get(request);
         break;
     case QtCoap::Method::Put:
         m_client->put(request, m_currentData);
         break;
     case QtCoap::Method::Post:
         m_client->post(request, m_currentData);
         break;
     case QtCoap::Method::Delete:
         m_client->deleteResource(request);
         break;
     default:
         break;
     }
     m_currentData.clear();
 }

 void MainWindow::on_discoverButton_clicked()
 {
     QUrl url;
     url.setHost(tryToResolveHostName(ui->hostComboBox->currentText()));
     url.setPort(ui->portSpinBox->value());

     QCoapResourceDiscoveryReply *discoverReply = m_client->discover(url, ui->discoveryPathEdit->text());
     if (discoverReply)
         connect(discoverReply, &QCoapResourceDiscoveryReply::discovered, this, &MainWindow::onDiscovered);
     else
         QMessageBox::critical(this, "Error", "Something went wrong, discovery request failed.");
 }

 void MainWindow::on_observeButton_clicked()
 {
     QUrl url;
     url.setHost(tryToResolveHostName(ui->hostComboBox->currentText()));
     url.setPort(ui->portSpinBox->value());
     url.setPath(ui->resourceComboBox->currentText());

     QCoapReply *observeReply = m_client->observe(url);
     if (!observeReply) {
         QMessageBox::critical(this, "Error", "Something went wrong, observe request failed.");
         return;
     }

     connect(observeReply, &QCoapReply::notified, this, &MainWindow::onNotified);

     ui->cancelObserveButton->setEnabled(true);
     connect(ui->cancelObserveButton, &QPushButton::clicked, this, [=]() {
         m_client->cancelObserve(url);
         ui->cancelObserveButton->setEnabled(false);
     });
 }

 void MainWindow::on_addOptionsButton_clicked()
 {
     OptionDialog dialog;
     if (dialog.exec() == QDialog::Accepted)
         m_options = dialog.options();
 }

 void MainWindow::on_contentButton_clicked()
 {
     QFileDialog dialog(this);
     dialog.setFileMode(QFileDialog::AnyFile);
     if (!dialog.exec())
         return;

     const auto fileName = dialog.selectedFiles().back();
     QFile file(fileName);
     if (!file.open(QIODevice::ReadOnly)) {
         QMessageBox::critical(this, "Error", QString("Failed to read from file %1").arg(fileName));
         return;
     }

     m_currentData = file.readAll();
 }

 void MainWindow::on_resourceComboBox_editTextChanged(const QString &text)
 {
     ui->observeButton->setEnabled(!text.isEmpty());
 }

 void MainWindow::on_methodComboBox_currentIndexChanged(int index)
 {
     Q_UNUSED(index);

     const auto method = ui->methodComboBox->currentData(Qt::UserRole).value<QtCoap::Method>();
     ui->contentButton->setEnabled(method == QtCoap::Method::Put || method == QtCoap::Method::Post);
 }