-
-
Notifications
You must be signed in to change notification settings - Fork 17
Let's code !
Here are exposed the main principles to write the code of your plugin. In example below, we suppose that the created plugin name is MyPlugin
.
Here is only discussed on the C++ plugin code. See How to start for more information.
Your plugin will be an executable, written in C++ language. Yadoms will start, stop and dial with it. A plugin can have several instances. This executable depends on (=link with) these libraries :
- plugin_cpp_api
- yadoms-shared
To make Yadoms recognize your plugin, your code must contain :
- A class implementing the
plugin_cpp_api::IPlugin
interface, which will do the main plugin job. - A declaration of the plugin, by a call to
IMPLEMENT_PLUGIN
macro (takes the class name as parameter)
Plugin is generally composed by these files (at least !) :
- MyPlugin.h : declaration of the class (CMyPlugin) implementing the
plugin_cpp_api::IPlugin
interface - MyPlugin.cpp : definition of CMyPlugin. Also declare the main plugin class with
IMPLEMENT_PLUGIN
macro. - MyPluginConfiguration.h : declaration of the configuration getter class (CMyPluginConfiguration)
- MyPluginConfiguration.cpp : definition of CMyPluginConfiguration
To make plugin_cpp_api
instance your plugin, you have to declare the main class using the IMPLEMENT_PLUGIN
macro.
In MyPlugin.cpp, simply call this macro like :
#include <plugin_cpp_api/ImplementationHelper.h>
IMPLEMENT_PLUGIN(CMyPlugin)
When user creates an instance of your plugin, Yadoms will start the plugin executable, construct the CMyPlugin
class, and call CMyPlugin::dowork
method.
We recommend to use an alias for the plugin API namespace (as in the rest of this example), adding this line in your MyPlugin.h file :
// Shortcut to yPluginApi namespace
namespace yApi = shared::plugin::yPluginApi;
In the rest of your code, you can then access to a function/object of the yPluginApi like : yApi::IDeviceCommand
This class manage all your plugin. It must implement plugin_cpp_api::IPlugin
, like :
#pragma once
#include <plugin_cpp_api/IPlugin.h>
#include "MyPluginConfiguration.h"
// Shortcut to yPluginApi namespace
namespace yApi = shared::plugin::yPluginApi;
class CMyPlugin: public plugin_cpp_api::IPlugin
{
public:
//--------------------------------------------------------------
/// \brief Constructor
//--------------------------------------------------------------
CMyPlugin();
//--------------------------------------------------------------
/// \brief Destructor
//--------------------------------------------------------------
virtual ~CMyPlugin();
// IPlugin implementation
virtual void doWork(boost::shared_ptr<yApi::IYPluginApi> api);
// [END] IPlugin implementation
private:
//--------------------------------------------------------------
/// \brief The plugin configuration
//--------------------------------------------------------------
CMyPluginConfiguration m_configuration;
};
Note that this class do only 2 things :
- Implement doWork function : all your working code will be here. This method is called by Yadoms when plugin instance starts.
- Store the plugin configuration
The dowork function contains the plugin main loop. This function receives an instantiation of the IYPluginApi (api
). This object allows your plugin to interact with Yadoms.
Your plugin will receive commands from Yadoms by the event handler (api->getEventHandler()
). This object enables to wait for a Yadoms command without CPU load.
The example for MyPlugin.cpp file, containing a simple doWork function :
#include "stdafx.h"
#include "MyPlugin.h"
#include <plugin_cpp_api/ImplementationHelper.h>
// Use this macro to define all necessary to make your library a Yadoms valid plugin.
// Note that you have to provide some extra files, like package.json, and icon.png
IMPLEMENT_PLUGIN(CMyPlugin)
CMyPlugin::CMyPlugin()
{
}
CMyPlugin::~CMyPlugin()
{
}
void CMyPlugin::doWork(boost::shared_ptr<yApi::IYPluginApi> api)
{
YADOMS_LOG(debug) << "CMyPlugin is starting...";
// Load configuration values (provided by database)
m_configuration.initializeWith(api->getConfiguration());
// Informs Yadoms about the plugin actual state
context->setPluginState(yApi::historization::EPluginState::kRunning);
// the main loop
while (1)
{
// Wait for an event
switch(api->getEventHandler().waitForEvents())
{
case yApi::IYPluginApi::kEventStopRequested:
{
// Yadoms request the plugin to stop. Note that plugin must be stop in 10 seconds max, otherwise it will be killed.
api->setPluginState(yApi::historization::EPluginState::kStopped);
return;
}
case yApi::IYPluginApi::kEventDeviceCommand:
{
// A command was received from Yadoms
...
break;
}
case yApi::IYPluginApi::kEventUpdateConfiguration:
{
// Configuration was updated
...
break;
}
default:
{
YADOMS_LOG(error) << "Unknown or unsupported message id " << api->getEventHandler().getEventId();
break;
}
}
}
api->setPluginState(yApi::historization::EPluginState::kStopped);
}
Note that the only way to stop gracefully a plugin is on Yadoms request (yApi::IYPluginApi::kEventStopRequested
event). All other exit ways will be considered as a plugin error.
You can use the event handler for custom events or timers.
First declare the event IDs (note that some values are reserved for Yadoms, so custom ID values must begin with yApi::IYPluginApi::kPluginFirstEventId
) :
enum
{
kCustomEvent1Id = yApi::IYPluginApi::kPluginFirstEventId,
kCustomEvent2Id
};
We can for example create a periodic timer, sending an event every 30 seconds to event handler :
context->getEventHandler().createTimer(kCustomEvent1Id, shared::event::CEventTimer::kPeriodic, boost::posix_time::seconds(30));
To receive the event, simply add, in the doWork function the case on your event ID :
void CMyPlugin::doWork(boost::shared_ptr<yApi::IYPluginApi> api)
{
try
{
...
while (1)
{
// Wait for an event
switch(api->getEventHandler().waitForEvents())
{
case yApi::IYPluginApi::kEventDeviceCommand:
...
break;
case yApi::IYPluginApi::kEventUpdateConfiguration:
...
break;
case kCustomEvent1Id:
... Put here the code to execute every 30 seconds ...
break;
default:
{
YADOMS_LOG(error) << "Unknown or unsupported message id " << api->getEventHandler().getEventId();
break;
}
}
}
}
catch (boost::thread_interrupted&)
{
YADOMS_LOG(information) << "Thread is stopping...";
}
}
We recommend you to write a configuration helper, the easier way to read a value from the configuration.
class CMyPluginConfiguration
{
public:
virtual ~CMyPluginConfiguration() {}
//--------------------------------------------------------------
/// \brief Load configuration data
/// \param [in] data The data container
//--------------------------------------------------------------
void initializeWith(const shared::CDataContainer &data)
{
m_configuration.initializeWith(data);
}
//--------------------------------------------------------------
// Your accessors
//--------------------------------------------------------------
std::string getConfiguredString() const
{
return m_configuration.get<std::string>("String");
}
std::string isAdvancedModeChecked() const
{
return m_configuration.get<bool>("AdvancedModeChecked");
}
private:
yApi::YPluginConfiguration m_configuration;
};
Note that the keys used here ("String", "AdvancedModeChecked") must be declared in the configuration schema in the package.json file.
##More help
To get more help, please visit our forum.
Yadoms -- The ultimate house automation solution