mirror of https://github.com/nymea/nymea.git
483 lines
20 KiB
Plaintext
483 lines
20 KiB
Plaintext
/*!
|
||
\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}.
|
||
|
||
*/
|
||
|
||
|