nymea/doc/tutorial2.qdoc

483 lines
20 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*!
\page tutorial2.html
\title Tutorial 2 - The "Buttons" plugin
\brief This plugin demonstrates the usage of events and actions.
\ingroup tutorials
In the second tutorial we make our own first plugin with the name \b {"Buttons"}. We will use this name for the naming concentions of the filenames.
\section1 Topics
This tutorial will show you how to:
\list
\li \unicode{0x25B6} Start with a new \l{DevicePlugin}{Plugin}
\li \unicode{0x25B6} Implement an \l{Action}
\li \unicode{0x25B6} Implement an \l{Event}
\endlist
In order to getting started with the new \b {"Button"} plugin we use the \b {"Minimal"} plugin as template and start from there. Make a copy of the minimal folder and name the new folder \b buttons-diy. In this case \b{buttons-diy} because the folder \b buttons already exits from the \tt plugin-template repository.
\section1 Create the basic structure
\code
$cp -rv minimal/ buttons-diy/
minimal/ -> buttons-diy/
minimal/plugins.pri -> buttons-diy/plugins.pri
minimal/minimal.pro -> buttons-diy/minimal.pro
minimal/devicepluginminimal.json -> buttons-diy/devicepluginminimal.json
minimal/minimal.pro.user -> buttons-diy/minimal.pro.user
minimal/devicepluginminimal.h -> buttons-diy/devicepluginminimal.h
minimal/devicepluginminimal.cpp -> buttons-diy/devicepluginminimal.cpp
\endcode
\note Delete the \tt minimal.pro.user file if there is any.
Now we can rename the files using the plugin name convention:
\code
$ cd buttons-diy/
$ mv minimal.pro buttons.pro
$ mv devicepluginminimal.h devicepluginbuttons.h
$ mv devicepluginminimal.cpp devicepluginbuttons.cpp
$ mv devicepluginminimal.json devicepluginbuttons.json
\endcode
\section2 Change the \tt buttons.pro
Open the \tt buttons.pro file with the \e {Qt Creator} and open that file in the editor:
\code
include(plugins.pri)
TARGET = $$qtLibraryTarget(guh_devicepluginminimal)
message("Building $$deviceplugin$${TARGET}.so")
SOURCES += \
devicepluginminimal.cpp \
HEADERS += \
devicepluginminimal.h \
\endcode
\list 1
\li Change the \tt TARGET name form \tt guh_devicepluginminimal \unicode{0x2192} \tt guh_devicepluginbuttons
\li Change the SOURCES file from \tt devicepluginminimal.cpp \unicode{0x2192} \tt devicepluginbuttons.cpp
\li Change the HEADERS file from \tt devicepluginminimal.h \unicode{0x2192} \tt devicepluginbuttons.h
\endlist
Your file sould look now like this:
\code
include(plugins.pri)
TARGET = $$qtLibraryTarget(guh_devicepluginbuttons)
message("Building $$deviceplugin$${TARGET}.so")
SOURCES += \
devicepluginbuttons.cpp \
HEADERS += \
devicepluginbuttons.h \
\endcode
If you save the file, the header and source file should appear in the project structure of the \e {Qt Creator}.
\section2 Change the \tt devicepluginbuttons.h
Open the \tt devicepluginbuttons.h file.
\code
#ifndef DEVICEPLUGINMINIMAL_H
#define DEVICEPLUGINMINIMAL_H
#include "plugin/deviceplugin.h"
#include "devicemanager.h"
class DevicePluginMinimal : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginminimal.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginMinimal();
DeviceManager::HardwareResources requiredHardware() const override;
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
};
#endif // DEVICEPLUGINMINIMAL_H
\endcode
\list 1
\li Change the \tt {#ifndef}, \tt {#define} and \tt #define name from \tt DEVICEPLUGINMINIMAL_H \unicode{0x2192} \tt DEVICEPLUGINBUTTONS_H
\li Change the class name form \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginButtons
\li Change in the \tt Q_PLUGIN_METADATA line the \tt FILE parameter from \tt "devicepluginminimal.json" \unicode{0x2192} \tt "devicepluginbuttons.json" to set \l{The Plugin JSON file}.
\li Change the constructor name from \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginButtons
\endlist
Your file sould look now like this:
\code
#ifndef DEVICEPLUGINBUTTONS_H
#define DEVICEPLUGINBUTTONS_H
#include "plugin/deviceplugin.h"
#include "devicemanager.h"
class DevicePluginButtons : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginbuttons.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginButtons();
DeviceManager::HardwareResources requiredHardware() const override;
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
};
#endif // DEVICEPLUGINBUTTONS_H
\endcode
\section2 Change the \tt devicepluginbuttons.cpp
Open the \tt devicepluginbuttons.cpp file.
\code
#include "devicepluginminimal.h"
#include "plugininfo.h"
DevicePluginMinimal::DevicePluginMinimal()
{
}
DeviceManager::HardwareResources DevicePluginMinimal::requiredHardware() const
{
return DeviceManager::HardwareResourceNone;
}
DeviceManager::DeviceSetupStatus DevicePluginMinimal::setupDevice(Device *device)
{
Q_UNUSED(device)
qCDebug(dcMinimal) << "Hello word! Setting up a new device:" << device->name();
qCDebug(dcMinimal) << "The new device has the DeviceId" << device->id().toString();
qCDebug(dcMinimal) << device->params();
return DeviceManager::DeviceSetupStatusSuccess;
}
\endcode
\list 1
\li Change the \tt {#include "devicepluginminimal.h"} \unicode{0x2192} \tt {#include "devicepluginbuttons.h"}
\li Change in each method implementation the \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginButtons namespace.
\li Change each \tt {qCDebug(dcMinimal)} \unicode{0x2192} \tt {qCDebug(dcButtons)}, you will see later why.
\endlist
Your file sould look now like this:
\code
#include "devicepluginbuttons.h"
#include "plugininfo.h"
DevicePluginButtons::DevicePluginButtons()
{
}
DeviceManager::HardwareResources DevicePluginButtons::requiredHardware() const
{
return DeviceManager::HardwareResourceNone;
}
DeviceManager::DeviceSetupStatus DevicePluginButtons::setupDevice(Device *device)
{
Q_UNUSED(device)
qCDebug(dcButtons) << "Hello word! Setting up a new device:" << device->name();
qCDebug(dcButtons) << "The new device has the DeviceId" << device->id().toString();
qCDebug(dcButtons) << device->params();
return DeviceManager::DeviceSetupStatusSuccess;
}
\endcode
The basic structure of our new \l{DevicePlugin} is finished. You may recognize that the \tt {plugininfo.h} file does not exist yet. You have to build the plugin to generate that file. Each time you change \l{The Plugin JSON file} this file will be new generated.
\section2 Change the \tt devicepluginbuttons.json
Our new plugin will have the name \b {Buttons}, the corresponding logging categorie will be \tt dcButtons (defined from the \e {idName}). There will be one new \l{DeviceClass} with the name \b {Simple Button}. This \l{DeviceClass} will have one \l{EventType} and one \l{ActionType}.
The current \tt devicepluginbuttons.json should still look like this:
\code
{
"name": "Minimal plugin",
"idName": "Minimal",
"id": "6878754a-f27d-4007-a4e5-b030b55853f5",
"vendors": [
{
"name": "Minimal vendor",
"idName": "minimal",
"id": "3897e82e-7c48-4591-9a2f-0f56c55a96a4",
"deviceClasses": [
{
"deviceClassId": "7014e5f1-5b04-407a-a819-bbebd11fa372",
"idName": "minimal",
"name": "Minimal device",
"createMethods": ["user"],
"paramTypes": [
{
"name": "name",
"type": "QString",
"defaultValue": "Simple button device default name"
}
]
}
]
}
]
}
\endcode
Now we change \l{The Plugin JSON file} for our new plugin:
\list 1
\li Set the plugin \e name to \b Buttons
\li Set the plugin \e idName to \b Buttons (used for logging category name -> \e {dcButtons})
\li Use \b {\tt uuidgen} to create a new UUID. Replace the old plugin \e id with the new one.
\li Set the vendor \e name to \b {Button vendor}
\li Set the vendor \e idName to \b buttons (used for VendorId variable definition in the \tt plugininfo.h -> \e buttonsVendorId})
\li Use \b {\tt uuidgen} to create a new UUID. Replace the old vendor \e id with the new one.
\li Use \b {\tt uuidgen} to create a new UUID. Replace the old DeviceClassId \e id with the new one.
\li Set the device class \e idName to \b simpleButton (used for DeviceClassId variable definition in the \tt plugininfo.h -> \e simpleButtonDeviceClassId})
\li Set the device class \e name to \b {Simple Button}
\li The \e createMethod is still the same: \b user
\li This single \l ParamType called \b name should be in every single DeviceClass to allow the user to give a custom name to a \l{Device}. Just change the defaultValue for the name to \b {Simple button device default name}.
\endlist
Your device should now look like this (with your own UUIDs):
\code
{
"name": "Buttons",
"idName": "Buttons",
"id": "7bfd3af5-7983-4540-9398-d14085d069f4",
"vendors": [
{
"name": "Button vendor",
"idName": "buttons",
"id": "fd2ae067-2c3d-4332-9c4b-ee0af653bcaf",
"deviceClasses": [
{
"deviceClassId": "73bb670b-e7a3-40da-bd6f-3260f017ec80",
"idName": "simpleButton",
"name": "Simple Button",
"createMethods": ["user"],
"paramTypes": [
{
"name": "name",
"type": "QString",
"defaultValue": "Simple button device default name"
}
]
}
]
}
]
}
\endcode
Now the basic structure is finished and we have a new \l{DevicePlugin}, a new \l{Vendor} and a new \l{DeviceClass}.
Now we have to add an \l{ActionType} which will called \b press and gives the user the possibility to press this button device (see \l{The ActionType definition})
\list 1
\li Use \b {\tt uuidgen} to create a new UUID for this \l{ActionType}.
\li Set the \e idName of this \l{ActionType} to \b pressSimpleButton (used for ActionTypeId variable definition in the \tt plugininfo.h -> \e pressSimpleButtonActionTypeId})
\li Set the \e name of this \l{ActionType} to \b {press the button}
\endlist
\code
"actionTypes": [
{
"id": "64c4ced5-9a1a-4858-81dd-1b5c94dba495",
"idName": "pressSimpleButton",
"name": "press the button"
}
]
\endcode
Now we have to add an \l{EventType} which will be emitted when the button was pressed.
\list 1
\li Use \b {\tt uuidgen} to create a new UUID for this \l{EventType}.
\li Set the \e idName of this \l{EventType} to \b simpleButtonPressed (used for EventTypeId variable definition in the \tt plugininfo.h -> \e simpleButtonPressedEventTypeId})
\li Set the \e name of this \l{EventType} to \b {button pressed}
\endlist
\code
"eventTypes": [
{
"id": "f9652210-9aed-4f38-8c19-2fd54f703fbe",
"idName": "simpleButtonPressed",
"name": "button pressed"
}
]
\endcode
\code
{
"name": "Buttons",
"idName": "Buttons",
"id": "7bfd3af5-7983-4540-9398-d14085d069f4",
"vendors": [
{
"name": "Button vendor",
"idName": "buttons",
"id": "fd2ae067-2c3d-4332-9c4b-ee0af653bcaf",
"deviceClasses": [
{
"deviceClassId": "73bb670b-e7a3-40da-bd6f-3260f017ec80",
"idName": "simpleButton",
"name": "Simple Button",
"createMethods": ["user"],
"paramTypes": [
{
"name": "name",
"type": "QString",
"defaultValue": "Simple button device default name"
}
],
"actionTypes": [
{
"id": "64c4ced5-9a1a-4858-81dd-1b5c94dba495",
"idName": "pressSimpleButton",
"name": "press the button"
}
],
"eventTypes": [
{
"id": "f9652210-9aed-4f38-8c19-2fd54f703fbe",
"idName": "simpleButtonPressed",
"name": "button pressed"
}
]
}
]
}
]
}
\endcode
Rebuild the entire project to generate the new \tt {plugininfo.h}. You need to call the \underline{Rebuild all} command in the \e {Qt Creator} to take over the changes in the \tt plugininfo.h .
If you make a syntax error in the JSON file, you will get a build error with the position of the syntax error in the JSON file. Now your definitions should be in the plugininfo.h file and ready to use in the plugin source code.
You will see in the build output following section:
\code
/usr/bin/guh-generateplugininfo ../buttons-diy/devicepluginbuttons.json plugininfo.h
../buttons-diy/devicepluginbuttons.json -> plugininfo.h
--> generate plugininfo.h
PluginId for plugin "Buttons" = 7bfd3af5-7983-4540-9398-d14085d069f4
define VendorId ButtonsVendorId = fd2ae067-2c3d-4332-9c4b-ee0af653bcaf
define DeviceClassId simpleButtonDeviceClassId = 73bb670b-e7a3-40da-bd6f-3260f017ec80
define logging category: "dcButtons"
--> generated successfully "plugininfo.h"
--> generate extern-plugininfo.h
--> generated successfully "extern-plugininfo.h"
\endcode
This shows you how the \tt{plugininfo.h} and \tt{extern-plugininfo.h} will be generated. As you can see the UUID definitions and the logging category were definend for the \b {Buttons} plugin.
Once the build step is finished, you can take a look at that file (curser in line \tt {#include "plugininfo.h"} and press \tt F2)
Your \tt plugininfo.h should now look like this (with your own UUIDs):
\code
#ifndef PLUGININFO_H
#define PLUGININFO_H
#include "typeutils.h"
#include <QLoggingCategory>
// Id definitions
PluginId pluginId = PluginId("7bfd3af5-7983-4540-9398-d14085d069f4");
VendorId buttonsVendorId = VendorId("fd2ae067-2c3d-4332-9c4b-ee0af653bcaf");
DeviceClassId simpleButtonDeviceClassId = DeviceClassId("73bb670b-e7a3-40da-bd6f-3260f017ec80");
ActionTypeId pressSimpleButtonActionTypeId = ActionTypeId("64c4ced5-9a1a-4858-81dd-1b5c94dba495");
EventTypeId simpleButtonPressedEventTypeId = EventTypeId("f9652210-9aed-4f38-8c19-2fd54f703fbe");
// Loging category
Q_DECLARE_LOGGING_CATEGORY(dcButtons)
Q_LOGGING_CATEGORY(dcButtons, "Buttons")
#endif // PLUGININFO_H
\endcode
\section1 Writing the plugin
Now we have our basic for starting to implement the new plugin. If you install the current plugin, you would already see the plugin implementation in \tt guh-cli, but it would do nothing because we have not implemented yet the code.
\section2 The \tt executeAction method
Every plugin with \l{Action}{Actions} needs the \l{DevicePlugin::executeAction()} method which should be overridden in your own plugin.
\code
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
\endcode
Here is the implemented method:
\code
// Check the DeviceClassId for "Simple Button"
if (device->deviceClassId() == simpleButtonDeviceClassId ) {
// check if this is the "press" action
if (action.actionTypeId() == pressSimpleButtonActionTypeId) {
qCDebug(dcButtons) << "Simple button" << device->paramValue("name").toString() << "was pressed";
// Emit the "button pressed" event
Event event(simpleButtonPressedEventTypeId, device->id());
emit emitEvent(event);
return DeviceManager::DeviceErrorNoError;
}
return DeviceManager::DeviceErrorActionTypeNotFound;
}
\endcode
When a user or the \l {guhserver::RuleEngine}{RuleEngine} calls the executeAction method, our plugin will first check if the \l DeviceClassId matches, then the \l ActionTypeId. If both are correct, we can emit our \l Event to show that the simple button \l Device was pressed.
You can see in the implementation that a new \l{Event} will be generated in guh. This is the way how you emit an Event for a device.
\section1 Test the plugin
Rebuild the whole project to make shore all changes are registered and install the plugin (see \l{Install the plugin}{Tutorial 1 - Install the plugin}).
\list 1
\li Start guh with following command:
\code
$ guhd -n -d Buttons
\endcode
\li Start guh-cli and add the a new \b {"Simple Button"} devcice. Give an appropriate name like \b {Test button}.
\li Use guh-cli to execute the \b {press the button} action:
\tt "Devices" \unicode{0x2192} \tt {"Execute an action"} \unicode{0x2192} \tt {"Your device name (Simple Button)"} \unicode{0x2192} \tt {press the button}
\endlist
\code
...
Connection: Tcp server: new client connected: "127.0.0.1"
Buttons: Simple button "Test button" was pressed
RuleEngine: got event: Event(EventTypeId: "{f9652210-9aed-4f38-8c19-2fd54f703fbe}",
DeviceId"{967d4c50-7cc5-4114-865a-822c64a1e7ce}") "Simple Button"
QUuid ("{f9652210-9aed-4f38-8c19-2fd54f703fbe}")
\endcode
Now you have successfully implemented you first DeviceClass, which has an \l Action and an \l Event and can be used together with the \l {guhserver::RuleEngine}{RuleEngine}.
Now you can take a look at \l{Tutorial 3 - The "Power Button" device}.
*/