add tutorial 6 code
This commit is contained in:
parent
c037f09485
commit
fe63117633
@ -9,7 +9,7 @@
|
||||
The \l{DeviceClass::SetupMethod}{SetupMethod} describes how the device will be set up. A \l{Device} can have multiple \l{DeviceClass::CreateMethods}{CreateMethods}, but only one \l{DeviceClass::SetupMethod}{SetupMethod}.
|
||||
|
||||
\list
|
||||
\li - \l{DeviceClass::CreateMethods}{CreateMethods}
|
||||
\li \l{DeviceClass::CreateMethods}{CreateMethods}
|
||||
\list
|
||||
\li \e user \unicode{0x2192} \l{DeviceClass::CreateMethodUser}
|
||||
\li \e discovery \unicode{0x2192} \l{DeviceClass::CreateMethodDiscovery}
|
||||
@ -168,15 +168,15 @@
|
||||
\endlist
|
||||
\li \b Pairing
|
||||
\list
|
||||
\li \b 4. | The user should see now the pin on the display and the pairing info. The method \l{DevicePlugin::confirmPairing()} will be called once he entered the pin. Here can be verified if the pin is authorized by the device and if the pairing succeeded.
|
||||
\li \b 5. | Returns the \l{DeviceManager::DeviceSetupStatus} to inform about the result (sync or async).
|
||||
\li \b 6. | Once the pairing has been verified (check if the button has been pushed) the plugin can emit the signal \l{DevicePlugin::pairingFinished()} to inform the \l{DeviceManager} about the result.
|
||||
\li \b 6. | The user should see now the pin on the display and the pairing info. The method \l{DevicePlugin::confirmPairing()} will be called once he entered the pin. Here can be verified if the pin is authorized by the device and if the pairing succeeded.
|
||||
\li \b 7. | Returns the \l{DeviceManager::DeviceSetupStatus} to inform about the result (sync or async).
|
||||
\li \b 8. | Once the pairing has been verified (check if the button has been pushed) the plugin can emit the signal \l{DevicePlugin::pairingFinished()} to inform the \l{DeviceManager} about the result.
|
||||
\endlist
|
||||
\li \b Setup
|
||||
\list
|
||||
\li \b 7. | The plugin will be set up with the params (i.e. containing a \tt pin param) of the paired \l{Device}.
|
||||
\li \b 8. | If something goes wrong during the setup return \l{DeviceManager::DeviceSetupStatusFailure}, otherwise \l{DeviceManager::DeviceSetupStatusSuccess}.
|
||||
\li \b 9. | If the device setup succeded and the device is in the system, the \l{DeviceManager} will call the \l{DevicePlugin::postSetupDevice()}{postSetupDevice()} method.
|
||||
\li \b 9. | The plugin will be set up with the params (i.e. containing a \tt pin param) of the paired \l{Device}.
|
||||
\li \b 10. | If something goes wrong during the setup return \l{DeviceManager::DeviceSetupStatusFailure}, otherwise \l{DeviceManager::DeviceSetupStatusSuccess}.
|
||||
\li \b 11. | If the device setup succeded and the device is in the system, the \l{DeviceManager} will call the \l{DevicePlugin::postSetupDevice()}{postSetupDevice()} method.
|
||||
\endlist
|
||||
\endlist
|
||||
*/
|
||||
|
||||
@ -191,6 +191,7 @@
|
||||
\li \e enterPin \unicode{0x2192} \l{DeviceClass::SetupMethodEnterPin}
|
||||
\li \e pushButton \unicode{0x2192} \l{DeviceClass::SetupMethodPushButton}
|
||||
\endlist
|
||||
\note For more information please take a look at \l{CreateMethods and SetupMethods} documentation.
|
||||
\li - \underline{\e pairingInfo:} The \l{DeviceClass::pairingInfo()}{pairingInfo} will inform the user how to pair the device \unicode{0x2192} \l{DeviceClass::setupMethod()}. This parameter will only be used for \l{DeviceClass::SetupMethodDisplayPin}{DisplayPin} and \l{DeviceClass::SetupMethodEnterPin}{EnterPin} and \l{DeviceClass::SetupMethodPushButton}{PushButton}. Example: "Please press the button on the device before continue."
|
||||
\li - \underline{\e discoveryParamTypes:} A list of \l{ParamType}{ParamTypes} which will be needed for discovering a device \unicode{0x2192} \l{DeviceClass::discoveryParamTypes()}. This parameter will only be used for devices with the \l{DeviceClass::CreateMethodDiscovery}{CreateMethodDiscovery} (see section "\l{The ParamType definition}").
|
||||
\li - \underline{\e paramTypes:} A list of \l{ParamType}{ParamTypes} which define the paramters of a device \unicode{0x2192} \l{DeviceClass::paramTypes()} (see section "\l{The ParamType definition}").
|
||||
|
||||
@ -9,15 +9,425 @@
|
||||
\list
|
||||
\li \unicode{0x25B6} Allow only one \l{Device}
|
||||
\li \unicode{0x25B6} Implement the \l{DevicePlugin::deviceRemoved()}{deviceRemoved()} method
|
||||
\li \unicode{0x25B6} Use plugin configurations
|
||||
\li \unicode{0x25B6} Use the \l{Coap}{CoAP} library
|
||||
\endlist
|
||||
|
||||
This tutorial shows you how to write a \l{Coap}{CoAP} plugin and how the plugin configuration work. The plugin it self has no practical purpose but shows some concepts of CoAP and plugin development.
|
||||
|
||||
\section1 Plugin structure
|
||||
\section1 The plugin source code
|
||||
|
||||
\section2 networkinfo.pro
|
||||
|
||||
\code
|
||||
include(plugins.pri)
|
||||
|
||||
TARGET = guh_deviceplugincoapclient
|
||||
|
||||
message(============================================)
|
||||
message("Qt version: $$[QT_VERSION]")
|
||||
message("Building $$deviceplugin$${TARGET}.so")
|
||||
|
||||
SOURCES += \
|
||||
deviceplugincoapclient.cpp \
|
||||
|
||||
HEADERS += \
|
||||
deviceplugincoapclient.h \
|
||||
|
||||
\endcode
|
||||
|
||||
\section2 devicepluginnetworkinfo.json
|
||||
|
||||
\code
|
||||
{
|
||||
"name": "Coap Client",
|
||||
"idName": "CoapClient",
|
||||
"id": "9ecadcbb-8699-41c2-a2e3-fd51a1faf1a1",
|
||||
"paramTypes": [
|
||||
{
|
||||
"name": "url",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": "coap://vs0.inf.ethz.ch:5683"
|
||||
}
|
||||
],
|
||||
"vendors": [
|
||||
{
|
||||
"id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
|
||||
"name": "guh",
|
||||
"idName": "guh",
|
||||
"deviceClasses": [
|
||||
{
|
||||
"deviceClassId": "69dcccbd-a66a-4c5b-8921-2fb86c4c4299",
|
||||
"idName": "info",
|
||||
"name": "Coap Client",
|
||||
"createMethods": ["user"],
|
||||
"basicTags": [
|
||||
"Service",
|
||||
"Sensor",
|
||||
"Actuator"
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "b8433a82-cf83-424f-b4a2-3f6507405d6c",
|
||||
"idName": "notifications",
|
||||
"name": "notification",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"writable": true
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "9aa31838-b62f-43b3-bdcd-8165840b5edf",
|
||||
"name": "upload message",
|
||||
"idName": "upload",
|
||||
"paramTypes": [
|
||||
{
|
||||
"name": "message",
|
||||
"type": "QString",
|
||||
"defaultValue": "Hallo world!"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"eventTypes": [
|
||||
{
|
||||
"name": "time changed",
|
||||
"idName": "time",
|
||||
"id": "44513802-138e-42f8-86a6-9edd4df77535",
|
||||
"paramTypes": [
|
||||
{
|
||||
"name": "time",
|
||||
"type": "QString"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section2 devicepluginnetworkinfo.h
|
||||
|
||||
\code
|
||||
#ifndef DEVICEPLUGINCOAPCLIENT_H
|
||||
#define DEVICEPLUGINCOAPCLIENT_H
|
||||
|
||||
#include "devicemanager.h"
|
||||
#include "plugin/deviceplugin.h"
|
||||
#include "coap/coap.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class DevicePluginCoapClient : public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "deviceplugincoapclient.json")
|
||||
Q_INTERFACES(DevicePlugin)
|
||||
|
||||
public:
|
||||
explicit DevicePluginCoapClient();
|
||||
|
||||
DeviceManager::HardwareResources requiredHardware() const override;
|
||||
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
|
||||
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
|
||||
private:
|
||||
QPointer<Device> m_device;
|
||||
QPointer<Coap> m_coap;
|
||||
|
||||
// Replies from coap
|
||||
QHash<CoapReply *, Device *> m_discoverReplies;
|
||||
QHash<CoapReply *, Device *> m_notificationEnableReplies;
|
||||
QHash<CoapReply *, Device *> m_notificationDisableReplies;
|
||||
QList<CoapReply *> m_uploadReplies;
|
||||
|
||||
QHash< CoapReply *, ActionId> m_asyncActions;
|
||||
|
||||
private slots:
|
||||
void onReplyFinished(CoapReply *reply);
|
||||
void onNotificationReceived(const CoapObserveResource &resource, const int ¬ificationNumber, const QByteArray &payload);
|
||||
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINNETWORKINFO_H
|
||||
\endcode
|
||||
|
||||
\section2 devicepluginnetworkinfo.cpp
|
||||
|
||||
\code
|
||||
#include "deviceplugincoapclient.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "coap/corelinkparser.h"
|
||||
|
||||
// Note: You can find the documentation for this code here -> http://dev.guh.guru/write-plugins.html
|
||||
|
||||
// The constructor of this device plugin.
|
||||
DevicePluginCoapClient::DevicePluginCoapClient()
|
||||
{
|
||||
}
|
||||
|
||||
DeviceManager::HardwareResources DevicePluginCoapClient::requiredHardware() const
|
||||
{
|
||||
return DeviceManager::HardwareResourceNone;
|
||||
}
|
||||
|
||||
DeviceManager::DeviceSetupStatus DevicePluginCoapClient::setupDevice(Device *device)
|
||||
{
|
||||
// Check if we already have a coap client device
|
||||
if (!myDevices().isEmpty()) {
|
||||
qCWarning(dcCoapClient) << "There is already a configured coap client device";
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
qCDebug(dcCoapClient) << "Setting up a new device:" << device->name() << device->params();
|
||||
|
||||
// Verify the given URL
|
||||
QUrl url(device->paramValue("url").toString());
|
||||
if (url.scheme() != "coap") {
|
||||
qCWarning(dcCoapClient) << "Invalid URL scheme" << url.scheme() << " != " << "coap";
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
m_device = device;
|
||||
|
||||
// Create new CoAP client if there isn't one yet
|
||||
if (m_coap.isNull()) {
|
||||
m_coap = new Coap(this);
|
||||
connect(m_coap, &Coap::replyFinished, this, &DevicePluginCoapClient::onReplyFinished);
|
||||
connect(m_coap, &Coap::notificationReceived, this, &DevicePluginCoapClient::onNotificationReceived);
|
||||
}
|
||||
|
||||
// Discover the CoAP server
|
||||
url.setPath("/.well-known/core");
|
||||
CoapReply *reply = m_coap->get(CoapRequest(url));
|
||||
|
||||
// Check imediatly if the there occured any error
|
||||
if (reply->error() != CoapReply::NoError) {
|
||||
qCWarning(dcCoapClient) << "Could not discover CoAP server:" << reply->errorString();
|
||||
reply->deleteLater();
|
||||
m_coap->deleteLater();
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
// Store the reply and device until we get our asynchronous response
|
||||
m_discoverReplies.insert(reply, device);
|
||||
|
||||
// Tell the DeviceManager that the setup result will be communicated later
|
||||
return DeviceManager::DeviceSetupStatusAsync;
|
||||
}
|
||||
|
||||
void DevicePluginCoapClient::deviceRemoved(Device *device)
|
||||
{
|
||||
// Prevent the unused variable warning
|
||||
Q_UNUSED(device)
|
||||
|
||||
// Delete the CoAP socket if not longer needed
|
||||
m_coap->deleteLater();
|
||||
}
|
||||
|
||||
// This method will be called whenever a client or the rule engine wants to execute an action for the given device.
|
||||
DeviceManager::DeviceError DevicePluginCoapClient::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
qCDebug(dcCoapClient) << "Execute action" << action.id() << action.params();
|
||||
|
||||
// check if the requested action is our "upload" action ...
|
||||
if (action.actionTypeId() == notificationsActionTypeId) {
|
||||
|
||||
// observe resource (enable notifications)
|
||||
QUrl url(device->paramValue("url").toString());
|
||||
url.setPath(url.path().append("/obs"));
|
||||
|
||||
if (action.param("notification").value().toBool()) {
|
||||
qCDebug(dcCoapClient) << "Enable notification on resource" << url.toString();
|
||||
CoapReply *reply = m_coap->enableResourceNotifications(CoapRequest(url));
|
||||
m_asyncActions.insert(reply, action.id());
|
||||
m_notificationEnableReplies.insert(reply, device);
|
||||
} else {
|
||||
qCDebug(dcCoapClient) << "Disable notification on resource" << url.toString();
|
||||
CoapReply *reply = m_coap->disableNotifications(CoapRequest(url));
|
||||
m_asyncActions.insert(reply, action.id());
|
||||
m_notificationDisableReplies.insert(reply, device);
|
||||
}
|
||||
|
||||
// Tell the DeviceManager that this is an async action and the
|
||||
// result of the execution will be emitted later.
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
|
||||
} else if (action.actionTypeId() == uploadActionTypeId) {
|
||||
|
||||
// Define the URL for uploading the message (POST)
|
||||
QUrl url(device->paramValue("url").toString());
|
||||
url.setPath(url.path().append("/test"));
|
||||
|
||||
// Upload the message (POST)
|
||||
CoapReply *reply = m_coap->post(CoapRequest(url), action.param("message").value().toString().toUtf8());
|
||||
m_uploadReplies.append(reply);
|
||||
m_asyncActions.insert(reply, action.id());
|
||||
|
||||
// Tell the DeviceManager that this is an async action and the
|
||||
// result of the execution will be emitted later.
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
|
||||
// ...otherwise the ActionType does not exist
|
||||
return DeviceManager::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
// This slot will be called whenever a reply from the CoAP socket has finished
|
||||
void DevicePluginCoapClient::onReplyFinished(CoapReply *reply)
|
||||
{
|
||||
// Now check which reply this was by checking in which Hash it can be found
|
||||
if (m_discoverReplies.keys().contains(reply)) {
|
||||
Device *device = m_discoverReplies.take(reply);
|
||||
|
||||
// Verify there where no reply errors (transport layer)
|
||||
if (reply->error() != CoapReply::NoError) {
|
||||
qCWarning(dcCoapClient) << "CoAP resource discovery reply error" << reply->errorString();
|
||||
reply->deleteLater();
|
||||
// Something went wrong during the discovery. Finish the setup with error.
|
||||
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify we have the right status code (server response)
|
||||
if (reply->statusCode() != CoapPdu::Content) {
|
||||
qCWarning(dcCoapClient) << "CoAP discovery status code:" << reply;
|
||||
reply->deleteLater();
|
||||
// Something went wrong during the discovery. Finish the setup with error.
|
||||
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcCoapClient) << "Discovered successfully the resources";
|
||||
|
||||
// Print the CoRE links we got from the server resource discovery
|
||||
CoreLinkParser parser(reply->payload());
|
||||
foreach (const CoreLink &link, parser.links()) {
|
||||
qCDebug(dcCoapClient) << link << endl;
|
||||
}
|
||||
|
||||
// Tell the device manager that the device setup finished successfully
|
||||
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
|
||||
|
||||
} else if (m_notificationEnableReplies.keys().contains(reply)) {
|
||||
Device *device = m_notificationEnableReplies.take(reply);
|
||||
ActionId actionId = m_asyncActions.take(reply);
|
||||
|
||||
// Verify there where no reply errors (transport layer)
|
||||
if (reply->error() != CoapReply::NoError) {
|
||||
qCWarning(dcCoapClient) << "CoAP enable observe resource reply error" << reply->errorString();
|
||||
// Something went wrong. Tell the devicemanager that the action finished with error.
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify we have the right status code (server response)
|
||||
if (reply->statusCode() != CoapPdu::Content) {
|
||||
qCWarning(dcCoapClient) << "CoAP enable observe status code:" << reply;
|
||||
// Something went wrong. Tell the devicemanager that the action finished with error.
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcCoapClient) << "Enabled successfully notifications" << reply;
|
||||
|
||||
// Set the corresping state
|
||||
device->setStateValue(notificationsStateTypeId, true);
|
||||
|
||||
// Tell the device manager that the action execution finished successfully
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
|
||||
|
||||
} else if (m_notificationDisableReplies.keys().contains(reply)) {
|
||||
Device *device = m_notificationDisableReplies.take(reply);
|
||||
ActionId actionId = m_asyncActions.take(reply);
|
||||
|
||||
// Verify there where no reply errors (transport layer)
|
||||
if (reply->error() != CoapReply::NoError) {
|
||||
qCWarning(dcCoapClient) << "CoAP disable observe resource reply error" << reply->errorString();
|
||||
// Something went wrong. Tell the devicemanager that the action finished with error.
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify we have the right status code (server response)
|
||||
if (reply->statusCode() != CoapPdu::Content) {
|
||||
qCWarning(dcCoapClient) << "CoAP disable observe status code:" << reply;
|
||||
// Something went wrong. Tell the devicemanager that the action finished with error.
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcCoapClient) << "Disabled successfully notifications" << reply;
|
||||
|
||||
// Set the corresping state
|
||||
device->setStateValue(notificationsStateTypeId, false);
|
||||
|
||||
// Tell the device manager that the action execution finished successfully
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
|
||||
|
||||
} else if (m_uploadReplies.contains(reply)) {
|
||||
ActionId actionId = m_asyncActions.take(reply);
|
||||
|
||||
// Verify there where no reply errors (transport layer)
|
||||
if (reply->error() != CoapReply::NoError) {
|
||||
qCWarning(dcCoapClient) << "CoAP upload reply error" << reply->errorString();
|
||||
// Something went wrong. Tell the devicemanager that the action finished with error.
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify we have the right status code (server response)
|
||||
if (reply->statusCode() != CoapPdu::Created) {
|
||||
qCWarning(dcCoapClient) << "CoAP upload status code:" << reply;
|
||||
// Something went wrong. Tell the devicemanager that the action finished with error.
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcCoapClient) << "Uploaded message successfully" << reply;
|
||||
|
||||
// Tell the device manager that the action execution finished successfully
|
||||
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
|
||||
|
||||
}
|
||||
|
||||
// Always make sure the reply will be deleted
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
// This method will be called if the CoAP socket received a notification from an observed resource
|
||||
void DevicePluginCoapClient::onNotificationReceived(const CoapObserveResource &resource, const int ¬ificationNumber, const QByteArray &payload)
|
||||
{
|
||||
qCDebug(dcCoapClient) << "Got notification from observed resource" << notificationNumber << resource.url().path() << endl << payload;
|
||||
|
||||
// Create the params for the event
|
||||
ParamList paramList;
|
||||
paramList.append(Param("time", payload));
|
||||
|
||||
// Tell the device manager we got an event
|
||||
emitEvent(Event(timeEventTypeId, m_device->id(), paramList));
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section1 Test the plugin
|
||||
|
||||
networkinfo.pro
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@ -38,7 +38,6 @@
|
||||
\list
|
||||
\li Allow only one \l{Device}
|
||||
\li Implement the \l{DevicePlugin::deviceRemoved()}{deviceRemoved()} method
|
||||
\li Use \l{DevicePlugin}{Plugin} configurations
|
||||
\li Use the \l{Coap}{CoAP} library
|
||||
\endlist
|
||||
\endlist
|
||||
|
||||
Reference in New Issue
Block a user