• 2. Your very first plugin
  • 2. Your very first plugin

    This is a nice example for showing the very basic concept of the nymea plugin mechanism.

    Lets say we want to create a Button device, called Simple button, which has a press Action and emits an Event called pressed when someone executes the press Action.

    Based on this Event we can create a rule and we have a virtual button which can be connected to any Action available in the entire system.

    Create the plugin structure

    Assuming you have read The plugin wizard tutorial and you have installed all build dependencies we start to create a new plugin project.

    In this image sequence you can see how this example was created.

    Define the plugin properties

    In order to start with the development, you have to take a closer look at the devicepluginsimplebutton.json file. In this file you can update the definitions of the DevicePlugin, Vendor and DeviceClass.

    The full documentation of this plugin definition file and the properties can be found in The plugin JSON File documentation.

    {
        "name": "SimpleButton",
        "displayName": "Simple button",
        "id": "28c7b102-3ac8-41f6-8dc0-f4787222a186",
        "vendors": [
            {
                "name": "guh",
                "displayName": "nymea",
                "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
                "deviceClasses": [
                    {
                        "name": "simplebutton",
                        "displayName": "Simple button",
                        "id": "c16ba02d-c982-4b45-8ca2-1945d94d8e66",
                        "deviceIcon": "None",
                        "setupMethod": "JustAdd",
                        "createMethods": ["User"],
                        "interfaces": [ "simplebutton" ],
                        "basicTags": [ ],
                        "paramTypes": [
    
                        ],
                        "stateTypes":[
    
                        ],
                        "actionTypes":[
                            {
                                "id": "64c4ced5-9a1a-4858-81dd-1b5c94dba495",
                                "name": "press",
                                "displayName": "press"
                            }
                        ],
                        "eventTypes":[
                            {
                                "id": "f9652210-9aed-4f38-8c19-2fd54f703fbe",
                                "name": "pressed",
                                "displayName": "button pressed"
                            }
                        ]
                    }
                ]
            }
        ]
    }

    First you have to update the plugin, vendor and device class id in the id fields. The wizard initializes these uuids with the default zero uuid. You can use the uuidgen command to create a new uuid and copy paste it into the id section.

    Note: You have to rebuild the whole plugin once you update the deviceplugin JSON file.

    The plugin definition

    As you can see in this example, the plugin name is called SimpleButton, and will be displayed as Simple button plugin in the client applications. The name can also be translated. The name will be used to define the debug category for your plugin. In this example the debug categorie will be defined as dcSimpleButton and can be used like following:

    qCDebug(dcSimpleButton()) << "This is a debug information.";
    qCWarning(dcSimpleButton()) << "This is a debug warning.";
    qCCritical(dcSimpleButton()) << "This is a critical debug warning.";

    The resulting debug output will look like following if the category is enabled:

    $ nymead -n -d SimpleButton
    
    ...
    
    I | SimpleButton: This is a debug information.
    W | SimpleButton: This is a debug warning.
    C | SimpleButton: This is a critical debug warning.

    This makes it easy to enable / disable the debug output of your plugin.

    The device class definition

    The Vendor section has not changed, since this example was developed from the guh Vendor.

    In order to give this simple button the required action and event described in the beginning of this tutorial, we have to define the appropriate types:

                "actionTypes":[
                    {
                        "id": "64c4ced5-9a1a-4858-81dd-1b5c94dba495",
                        "name": "press",
                        "displayName": "press"
                    }
                ],

    This is the most simple action you can create. The name property definies how the action will be called in the system. The displayName definies the string how the action will be displayed to the user and in the client applications.

                "eventTypes":[
                    {
                        "id": "f9652210-9aed-4f38-8c19-2fd54f703fbe",
                        "name": "pressed",
                        "displayName": "button pressed"
                    }
                ]

    This is the most simple event you can create. The name property definies how the event will be called in the system. The displayName definies the string how the event will be displayed to the user and in the client applications.

    As you may have noticed, this example represents already an interface. The simplebutton interface offers a template for general simple buttons which are able to emit a pressed event. In order to make use of the interface you simply add the interface to the list.

    ...
    
    "interfaces": [ "simplebutton" ],
    
    ...

    The server will verify if the required types for an interface match the template. This will be evaluated once the server loads the plugin.

    Once you updated the JSON file, you have to rebuild the whole plugin in order to trigger a rebuild of the the plugin information include files.

    nymea-generateplugininfo

    Before the source code will be compiled, a precompiler called nymea-generateplugininfo will be launched. This tool will read the plugin JSON file and generate two header files for the plugin in the build directory.

    plugininfo.h

    As you can see here this file contains all uuid definitions and translation string generated from the plugin JSON file. This file will regenerated every time you

    /* This file is generated by the nymea build system. Any changes to this file will
     * be lost.
     *
     * If you want to change this file, edit the plugin's json file.
     */
    
    #ifndef PLUGININFO_H
    #define PLUGININFO_H
    
    #include <QLoggingCategory>
    #include <QObject>
    
    #include "typeutils.h"
    
    // Id definitions
    PluginId pluginId = PluginId("28c7b102-3ac8-41f6-8dc0-f4787222a186");
    VendorId guhVendorId = VendorId("2062d64d-3232-433c-88bc-0d33c0ba2ba6");
    DeviceClassId simplebuttonDeviceClassId = DeviceClassId("c16ba02d-c982-4b45-8ca2-1945d94d8e66");
    ActionTypeId simplebuttonPressActionTypeId = ActionTypeId("64c4ced5-9a1a-4858-81dd-1b5c94dba495");
    EventTypeId simplebuttonPressedEventTypeId = EventTypeId("f9652210-9aed-4f38-8c19-2fd54f703fbe");
    
    // Logging category
    Q_DECLARE_LOGGING_CATEGORY(dcSimpleButton)
    Q_LOGGING_CATEGORY(dcSimpleButton, "SimpleButton")
    
    // Translation strings
    const QString translations[] {
        //: The name of the plugin SimpleButton (28c7b102-3ac8-41f6-8dc0-f4787222a186)
        QT_TRANSLATE_NOOP("SimpleButton", "Simple button"),
    
        //: The name of the vendor (2062d64d-3232-433c-88bc-0d33c0ba2ba6)
        QT_TRANSLATE_NOOP("SimpleButton", "nymea"),
    
        //: The name of the DeviceClass (c16ba02d-c982-4b45-8ca2-1945d94d8e66)
        QT_TRANSLATE_NOOP("SimpleButton", "Simple button"),
    
        //: The name of the ActionType 64c4ced5-9a1a-4858-81dd-1b5c94dba495 of deviceClass simplebutton
        QT_TRANSLATE_NOOP("SimpleButton", "press"),
    
        //: The name of the EventType f9652210-9aed-4f38-8c19-2fd54f703fbe of deviceClass simplebutton
        QT_TRANSLATE_NOOP("SimpleButton", "button pressed")
    };
    
    #endif // PLUGININFO_H

    extern-plugininfo.h

    /* This file is generated by the nymea build system. Any changes to this file will
     * be lost.
     *
     * If you want to change this file, edit the plugin's json file.
     */
    
    #ifndef EXTERNPLUGININFO_H
    #define EXTERNPLUGININFO_H
    #include "typeutils.h"
    #include <QLoggingCategory>
    
    // Id definitions
    extern PluginId pluginId;
    extern VendorId guhVendorId;
    extern DeviceClassId simplebuttonDeviceClassId;
    extern ActionTypeId simplebuttonPressActionTypeId;
    extern EventTypeId simplebuttonPressedEventTypeId;
    
    // Logging category definition
    Q_DECLARE_LOGGING_CATEGORY(dcSimpleButton)
    
    #endif // EXTERNPLUGININFO_H

    Implement the plugin

    In order to implement this simple action and emit the Event once the button will be pressed, you need to implement following code:

    DeviceManager::DeviceError DevicePluginSimpleButton::executeAction(Device *device, const Action &action)
    {
        qCDebug(dcSimpleButton()) << "Executing action for device" << device->name() << action.actionTypeId().toString() << action.params();
    
        // Check the device class
        if (device->deviceClassId() == simplebuttonDeviceClassId) {
    
            // Check the action type
            if (action.actionTypeId() == simplebuttonPressActionTypeId) {
    
                // Emit the pressed event on button press
                qCDebug(dcSimpleButton()) << "Emit event pressed for simple button" << device->name();
                emitEvent(Event(simplebuttonPressedEventTypeId, device->id()));
                return DeviceManager::DeviceErrorNoError;
            }
    
            // Unhandled action type
            return DeviceManager::DeviceErrorActionTypeNotFound;
        }
    
        // Unhandled device type
        return DeviceManager::DeviceErrorDeviceClassNotFound;
    }

    In this code section you can see the implementation of the executeAction method for this example tutorial. The method will be called from DeviceManager once an Action should be executed. First we have to check for which device class this action is ment. The we have to check which action of this device class should be executed. In this example we have only one deviceclass and one action.

        emitEvent(Event(simplebuttonPressedEventTypeId, device->id()));

    And finally, once the action press gets called, the event pressed will be emitted in the system.

    This event can be connected whithin a rule to execute whatever action you want.

    Test the plugin

    Now it's time to build you first plugin. In order to make sure all changes in you JSON file are up to date press the Rebuild all instead of only Build. This will rerun the nymea-generateplugininfo tool and update the plugininfo.h and extern-plugininfo.h files.

    Note: Comming soon!

    Files: