add createmethod and setupmethod params

add support for createmethod "auto"
add functional boblight plugin
pull/1/head
Michael Zanetti 2014-04-13 10:27:08 +02:00
parent 9b4c28801c
commit 8d1c7f9c03
36 changed files with 686 additions and 72 deletions

View File

@ -100,6 +100,7 @@ DeviceManager::DeviceManager(QObject *parent) :
// Give hardware a chance to start up before loading plugins etc.
QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "loadConfiguredDevices", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "createNewAutoDevices", Qt::QueuedConnection);
// Make sure this is always emitted after plugins and devices are loaded
QMetaObject::invokeMethod(this, "loaded", Qt::QueuedConnection);
}
@ -111,11 +112,20 @@ QList<DevicePlugin *> DeviceManager::plugins() const
}
/*! Returns the \{DevicePlugin} with the given \a id. Null if the id couldn't be found. */
DevicePlugin *DeviceManager::plugin(const QUuid &id) const
DevicePlugin *DeviceManager::plugin(const PluginId &id) const
{
return m_devicePlugins.value(id);
}
void DeviceManager::setPluginConfig(const PluginId &pluginId, const QVariantMap &pluginConfig)
{
DevicePlugin *plugin = m_devicePlugins.value(pluginId);
plugin->setConfiguration(pluginConfig);
QSettings settings;
settings.setValue(plugin->pluginId().toString(), pluginConfig);
createNewAutoDevices();
}
QList<Vendor> DeviceManager::supportedVendors() const
{
return m_supportedVendors.values();
@ -142,6 +152,19 @@ QList<DeviceClass> DeviceManager::supportedDevices(const VendorId &vendorId) con
be generated.
*/
DeviceManager::DeviceError DeviceManager::addConfiguredDevice(const DeviceClassId &deviceClassId, const QVariantMap &params, const DeviceId id)
{
DeviceClass deviceClass = findDeviceClass(deviceClassId);
if (deviceClass.id().isNull()) {
qWarning() << "cannot find a device class with id" << deviceClassId;
return DeviceErrorDeviceClassNotFound;
}
if (deviceClass.createMethod() == DeviceClass::CreateMethodUser) {
return addConfiguredDeviceInternal(deviceClassId, params, id);
}
return DeviceErrorCreationNotSupported;
}
DeviceManager::DeviceError DeviceManager::addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QVariantMap &params, const DeviceId id)
{
DeviceClass deviceClass = findDeviceClass(deviceClassId);
if (deviceClass.id().isNull()) {
@ -182,7 +205,7 @@ DeviceManager::DeviceError DeviceManager::addConfiguredDevice(const DeviceClassI
return DeviceErrorNoError;
}
DeviceManager::DeviceError DeviceManager::removeConfiguredDevice(const QUuid &deviceId)
DeviceManager::DeviceError DeviceManager::removeConfiguredDevice(const DeviceId &deviceId)
{
Device *device = findConfiguredDevice(deviceId);
if (!device) {
@ -207,7 +230,7 @@ DeviceManager::DeviceError DeviceManager::removeConfiguredDevice(const QUuid &de
}
/*! Returns the \l{Device} with the given \a id. Null if the id couldn't be found. */
Device *DeviceManager::findConfiguredDevice(const QUuid &id) const
Device *DeviceManager::findConfiguredDevice(const DeviceId &id) const
{
foreach (Device *device, m_configuredDevices) {
if (device->id() == id) {
@ -224,7 +247,7 @@ QList<Device *> DeviceManager::configuredDevices() const
}
/*! Returns all \l{Device}{Devices} matching the \l{DeviceClass} referred by \a deviceClassId. */
QList<Device *> DeviceManager::findConfiguredDevices(const QUuid &deviceClassId) const
QList<Device *> DeviceManager::findConfiguredDevices(const DeviceClassId &deviceClassId) const
{
QList<Device*> ret;
foreach (Device *device, m_configuredDevices) {
@ -235,19 +258,6 @@ QList<Device *> DeviceManager::findConfiguredDevices(const QUuid &deviceClassId)
return ret;
}
/*! For conveninece, this returns the \{DeviceClass} that describes the \l{EventType} referred by \a eventTypeId. */
DeviceClass DeviceManager::findDeviceClassforEvent(const QUuid &eventTypeId) const
{
foreach (const DeviceClass &deviceClass, m_supportedDevices) {
foreach (const EventType &eventType, deviceClass.events()) {
if (eventType.id() == eventTypeId) {
return deviceClass;
}
}
}
return DeviceClass();
}
/*! For conveninece, this returns the \{DeviceClass} with the id given by \a deviceClassId.
Note: The returned DeviceClass may be invalid.*/
DeviceClass DeviceManager::findDeviceClass(const QUuid &deviceClassId) const
@ -275,14 +285,19 @@ DeviceManager::DeviceError DeviceManager::executeAction(const Action &action)
foreach (const ActionType &actionType, deviceClass.actions()) {
if (actionType.id() == action.actionTypeId()) {
found = true;
if (actionType.parameters().count() > action.params().count()) {
return DeviceErrorMissingParameter;
}
continue;
}
}
if (!found) {
return DeviceErrorActionTypeNotFound;
}
m_devicePlugins.value(device->pluginId())->executeAction(device, action);
return DeviceErrorNoError;
return m_devicePlugins.value(device->pluginId())->executeAction(device, action);
}
}
return DeviceErrorDeviceNotFound;
@ -313,6 +328,11 @@ void DeviceManager::loadPlugins()
m_supportedDevices.insert(deviceClass.id(), deviceClass);
qDebug() << "* Loaded device class:" << deviceClass.name();
}
QSettings settings;
if (settings.contains(pluginIface->pluginId().toString())) {
pluginIface->setConfiguration(settings.value(pluginIface->pluginId().toString()).toMap());
}
m_devicePlugins.insert(pluginIface->pluginId(), pluginIface);
connect(pluginIface, &DevicePlugin::emitEvent, this, &DeviceManager::emitEvent);
}
@ -350,6 +370,35 @@ void DeviceManager::storeConfiguredDevices()
}
}
void DeviceManager::createNewAutoDevices()
{
bool haveNewDevice = false;
foreach (const DeviceClass &deviceClass, m_supportedDevices) {
if (deviceClass.createMethod() != DeviceClass::CreateMethodAuto) {
continue;
}
qDebug() << "found auto device class" << deviceClass.name();
DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId());
bool success = false;
do {
QList<Device*> loadedDevices = findConfiguredDevices(deviceClass.id());
Device *device = new Device(plugin->pluginId(), DeviceId::createDeviceId(), deviceClass.id());
success = plugin->configureAutoDevice(loadedDevices, device);
if (success) {
qDebug() << "New device detected for" << deviceClass.name() << device->name();
setupDevice(device);
m_configuredDevices.append(device);
haveNewDevice = true;
} else {
qDebug() << "No newly detected devices for" << deviceClass.name();
delete device;
}
} while (success);
}
storeConfiguredDevices();
}
void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value)
{
Device *device = qobject_cast<Device*>(sender());

View File

@ -53,23 +53,27 @@ public:
DeviceErrorMissingParameter,
DeviceErrorPluginNotFound,
DeviceErrorSetupFailed,
DeviceErrorDuplicateUuid
DeviceErrorDuplicateUuid,
DeviceErrorCreationNotSupported,
DeviceErrorDeviceParameterError,
DeviceErrorActionParameterError
};
explicit DeviceManager(QObject *parent = 0);
QList<DevicePlugin*> plugins() const;
DevicePlugin* plugin(const QUuid &id) const;
DevicePlugin* plugin(const PluginId &id) const;
void setPluginConfig(const PluginId &pluginId, const QVariantMap &pluginConfig);
QList<Vendor> supportedVendors() const;
QList<DeviceClass> supportedDevices(const VendorId &vendorId = VendorId()) const;
QList<Device*> configuredDevices() const;
DeviceError addConfiguredDevice(const DeviceClassId &deviceClassId, const QVariantMap &params, const DeviceId id = DeviceId::createDeviceId());
DeviceError removeConfiguredDevice(const QUuid &deviceId);
DeviceError removeConfiguredDevice(const DeviceId &deviceId);
Device* findConfiguredDevice(const QUuid &id) const;
QList<Device*> findConfiguredDevices(const QUuid &deviceClassId) const;
DeviceClass findDeviceClassforEvent(const QUuid &eventTypeId) const;
Device* findConfiguredDevice(const DeviceId &id) const;
QList<Device*> findConfiguredDevices(const DeviceClassId &deviceClassId) const;
DeviceClass findDeviceClass(const QUuid &deviceClassId) const;
signals:
@ -84,6 +88,7 @@ private slots:
void loadPlugins();
void loadConfiguredDevices();
void storeConfiguredDevices();
void createNewAutoDevices();
// Only connect this to Devices. It will query the sender()
void slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value);
@ -92,6 +97,7 @@ private slots:
void timerEvent();
private:
DeviceError addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QVariantMap &params, const DeviceId id = DeviceId::createDeviceId());
bool setupDevice(Device *device);
QHash<VendorId, Vendor> m_supportedVendors;

View File

@ -42,7 +42,9 @@
DeviceClass::DeviceClass(const QUuid &pluginId, const VendorId &vendorId, const DeviceClassId &id):
m_id(id),
m_vendorId(vendorId),
m_pluginId(pluginId)
m_pluginId(pluginId),
m_createMethod(CreateMethodUser),
m_setupMethod(SetupMethodJustAdd)
{
}
@ -139,6 +141,26 @@ void DeviceClass::setParams(const QVariantList &params)
m_params = params;
}
DeviceClass::CreateMethod DeviceClass::createMethod() const
{
return m_createMethod;
}
void DeviceClass::setCreateMethod(DeviceClass::CreateMethod createMethod)
{
m_createMethod = createMethod;
}
DeviceClass::SetupMethod DeviceClass::setupMethod() const
{
return m_setupMethod;
}
void DeviceClass::setSetupMethod(DeviceClass::SetupMethod setupMethod)
{
m_setupMethod = setupMethod;
}
/*! Compare this \a deviceClass to another. This is effectively the same as calling a.id() == b.id(). Returns true if the ids match.*/
bool DeviceClass::operator==(const DeviceClass &deviceClass) const
{

View File

@ -31,6 +31,18 @@
class DeviceClass
{
public:
enum CreateMethod {
CreateMethodUser,
CreateMethodAuto,
CreateMethodDiscovery
};
enum SetupMethod {
SetupMethodJustAdd,
SetupMethodDisplayPin,
SetupMethodEnterPin,
SetupMethodPushButton
};
DeviceClass(const QUuid &pluginId = QUuid(), const VendorId &vendorId = VendorId(), const DeviceClassId &id = DeviceClassId());
DeviceClassId id() const;
@ -53,6 +65,11 @@ public:
QVariantList params() const;
void setParams(const QVariantList &params);
CreateMethod createMethod() const;
void setCreateMethod(CreateMethod createMethod);
SetupMethod setupMethod() const;
void setSetupMethod(SetupMethod setupMethod);
bool operator==(const DeviceClass &device) const;
private:
@ -64,6 +81,8 @@ private:
QList<EventType> m_events;
QList<ActionType> m_actions;
QVariantList m_params;
CreateMethod m_createMethod;
SetupMethod m_setupMethod;
};
#endif

View File

@ -104,6 +104,28 @@ DevicePlugin::~DevicePlugin()
}
/*! Override this if your plugin supports Device with DeviceClass::CreationMethodAuto.
This will be called at startup, after the configured devices have been loaded.
You should walk through loadedDevices and check whether all the detected devices
are contained in there. If all the detected devices are already contained, return
false. If instead you've found a new device which isn't known to the system yet,
fill in the parameters of the passed device with some details that makes it possible
for you to match this Device object with the detected hardware. After that, return true.
The DeviceManager will then insert the device into its database and call deviceCreated()
for this device. Therefore you should not do any hardware initialisation in this state yet
but rather wait for the subsequent deviceCreated() call to set it up like in any other
case where Device can be created.
Returning false will cause the passed device object to be destroyed.
If you have detected multiple new devices, just load them one by one. The DeviceManager
will continue to call this method until you return false.
*/
bool DevicePlugin::configureAutoDevice(QList<Device*> loadedDevices, Device *device) const
{
Q_UNUSED(loadedDevices)
Q_UNUSED(device)
return false;
}
/*! This will be called when a new device is created. The plugin has the chance to do some setup.
Return false if something bad happened during the setup. The device will be disabled.
*/

View File

@ -22,6 +22,7 @@
#include "devicemanager.h"
#include "deviceclass.h"
#include "typeutils.h"
#include "types/event.h"
#include "types/action.h"
#include "types/vendor.h"
@ -47,6 +48,8 @@ public:
virtual QList<DeviceClass> supportedDevices() const = 0;
virtual DeviceManager::HardwareResources requiredHardware() const = 0;
virtual bool configureAutoDevice(QList<Device *> loadedDevices, Device *device) const;
virtual bool deviceCreated(Device *device);
virtual void deviceRemoved(Device *device);
@ -58,7 +61,7 @@ public:
virtual void setConfiguration(const QVariantMap &configuration);
public slots:
virtual void executeAction(Device *device, const Action &action) {Q_UNUSED(device) Q_UNUSED(action)}
virtual DeviceManager::DeviceError executeAction(Device *device, const Action &action) {Q_UNUSED(device) Q_UNUSED(action)}
signals:

View File

@ -22,6 +22,7 @@ DECLARE_TYPE_ID(Device)
DECLARE_TYPE_ID(EventType)
DECLARE_TYPE_ID(StateType)
DECLARE_TYPE_ID(ActionType)
DECLARE_TYPE_ID(Plugin)
#endif // TYPEUTILS_H

View File

@ -0,0 +1,107 @@
#include "bobclient.h"
#include "libboblight/boblight.h"
#include <QDebug>
BobClient::BobClient(QObject *parent) :
QObject(parent),
m_connected(false),
m_port(-1)
{
m_lastSyncTime = QTime::currentTime();
m_resyncTimer.setInterval(200);
m_resyncTimer.setSingleShot(true);
QObject::connect(&m_resyncTimer, SIGNAL(timeout()), SLOT(sync()));
}
bool BobClient::connect(const QString &hostname, int port)
{
qDebug() << "Connecting to boblightd\n";
m_boblight = boblight_init();
//try to connect, if we can't then bitch to stderr and destroy boblight
if (!boblight_connect(m_boblight, hostname.toLatin1().data(), port, 5000000) ||
!boblight_setpriority(m_boblight, 1))
{
qDebug() << "Failed to connect:" << boblight_geterror(m_boblight);
boblight_destroy(m_boblight);
m_connected = false;
return false;
}
qDebug() << "Connection to boblightd opened\n";
m_hostname = hostname;
m_port = port;
m_connected = true;
return true;
}
bool BobClient::connected() const
{
return m_connected;
}
void BobClient::setPriority(int priority)
{
qDebug() << "setting new priority:" << priority;
qDebug() << "setting priority to" << priority << boblight_setpriority(m_boblight, priority);
}
void BobClient::setColor(int channel, QColor color)
{
if(channel == -1) {
for(int i = 0; i < lightsCount(); ++i) {
setColor(i, color);
}
} else {
m_colors[channel] = color;
// qDebug() << "set channel" << channel << "to color" << color;
}
}
void BobClient::sync()
{
if(!m_connected) {
qDebug() << "Not connected to boblight. Cannot sync";
return;
}
if(m_lastSyncTime.addMSecs(50) > QTime::currentTime()) {
if(!m_resyncTimer.isActive()) {
m_resyncTimer.start();
}
return;
}
//qDebug() << "syncing";
m_lastSyncTime = QTime::currentTime();
for(int i = 0; i < lightsCount(); ++i) {
//load the color into int array
int rgb[3] = {m_colors[i].red() * m_colors[i].alphaF(), m_colors[i].green() * m_colors[i].alphaF(), m_colors[i].blue() * m_colors[i].alphaF()};
// qDebug() << "set color" << rgb[0] << rgb[1] << rgb[2];
//set all lights to the color we want and send it
boblight_addpixel(m_boblight, i, rgb);
}
if (!boblight_sendrgb(m_boblight, 1, NULL)) //some error happened, probably connection broken, so bitch and try again
{
qDebug() << "Boblight connection error!";
qDebug() << boblight_geterror(m_boblight);
boblight_destroy(m_boblight);
m_connected = false;
connect(m_hostname, m_port);
}
}
int BobClient::lightsCount()
{
return boblight_getnrlights(m_boblight);
}
QColor BobClient::currentColor(int channel)
{
return m_colors[channel];
}

View File

@ -0,0 +1,40 @@
#ifndef BOBCLIENT_H
#define BOBCLIENT_H
#include <QObject>
#include <QTimer>
#include <QMap>
#include <QColor>
#include <QTime>
class BobClient : public QObject
{
Q_OBJECT
public:
explicit BobClient(QObject *parent = 0);
bool connect(const QString &hostname, int port);
bool connected() const;
int lightsCount();
QColor currentColor(int channel);
void setPriority(int priority);
signals:
public slots:
void setColor(int channel, QColor color);
void sync();
private:
QMap<int, QColor> m_colors; //channel, color
void *m_boblight;
bool m_connected;
QString m_hostname;
int m_port;
QTime m_lastSyncTime;
QTimer m_resyncTimer;
};
#endif // BOBCLIENT_H

View File

@ -0,0 +1,17 @@
include(../../plugins.pri)
TARGET = $$qtLibraryTarget(guh_devicepluginboblight)
INCLUDEPATH += /usr/local/include/
LIBS += -L/usr/local/lib/libboblight.a
SOURCES += \
devicepluginboblight.cpp \
bobclient.cpp \
HEADERS += \
devicepluginboblight.h \
bobclient.h \

View File

@ -0,0 +1,138 @@
#include "devicepluginboblight.h"
#include "plugin/device.h"
#include "devicemanager.h"
#include "bobclient.h"
#include <QDebug>
#include <QStringList>
VendorId boblightVendorId = VendorId("8c5e8d4c-b5ed-4bfe-b30d-35c2790ec100");
QUuid boblightPluginUuid = QUuid("e1647872-c0f5-4680-b49b-3924e5b54dcd");
DeviceClassId boblightDeviceClassId = DeviceClassId("1647c61c-db14-461e-8060-8a3533d5d92f");
StateTypeId colorStateTypeId = StateTypeId("97ec80cd-43a9-40fa-93b7-d1580043d981");
ActionTypeId setColorActionTypeId = ActionTypeId("668e1aa3-fa13-49ce-8630-17a5c0a7c34b");
DevicePluginBoblight::DevicePluginBoblight()
{
m_bobClient = new BobClient(this);
m_config.insert("boblighthost", "localhost");
m_config.insert("boblightport", "19333");
connectToBoblight();
}
QList<Vendor> DevicePluginBoblight::supportedVendors() const
{
QList<Vendor> ret;
Vendor guh(boblightVendorId, "http://code.google.com/p/boblight/");
ret.append(guh);
return ret;
}
QList<DeviceClass> DevicePluginBoblight::supportedDevices() const
{
QList<DeviceClass> ret;
DeviceClass deviceClassBoblight(pluginId(), boblightVendorId, boblightDeviceClassId);
deviceClassBoblight.setName("Boblight");
deviceClassBoblight.setCreateMethod(DeviceClass::CreateMethodAuto);
QList<StateType> boblightStates;
StateType colorState(colorStateTypeId);
colorState.setName("color");
colorState.setType(QVariant::Color);
colorState.setDefaultValue(QColor(Qt::black));
boblightStates.append(colorState);
deviceClassBoblight.setStates(boblightStates);
QList<ActionType> boblightActons;
ActionType setColorAction(setColorActionTypeId);
setColorAction.setName("Set color");
QVariantList actionParamsSetColor;
QVariantMap actionParamSetColor;
actionParamSetColor.insert("name", "color");
actionParamSetColor.insert("type", "color");
actionParamsSetColor.append(actionParamSetColor);
setColorAction.setParameters(actionParamsSetColor);
boblightActons.append(setColorAction);
deviceClassBoblight.setActions(boblightActons);
ret.append(deviceClassBoblight);
return ret;
}
DeviceManager::HardwareResources DevicePluginBoblight::requiredHardware() const
{
return DeviceManager::HardwareResourceNone;
}
bool DevicePluginBoblight::configureAutoDevice(QList<Device *> loadedDevices, Device *device) const
{
if (!m_bobClient->connected()) {
return false;
}
if (loadedDevices.count() < m_bobClient->lightsCount()) {
int index = loadedDevices.count();
device->setName("Boblight Channel " + QString::number(index));
QVariantMap params;
params.insert("channel", index);
device->setParams(params);
device->setStateValue(colorStateTypeId, m_bobClient->currentColor(index));
return true;
}
return false;
}
QString DevicePluginBoblight::pluginName() const
{
return "Boblight client";
}
QUuid DevicePluginBoblight::pluginId() const
{
return boblightPluginUuid;
}
QVariantMap DevicePluginBoblight::configuration() const
{
return m_config;
}
void DevicePluginBoblight::setConfiguration(const QVariantMap &configuration)
{
m_config = configuration;
connectToBoblight();
}
DeviceManager::DeviceError DevicePluginBoblight::executeAction(Device *device, const Action &action)
{
if (!m_bobClient->connected()) {
return DeviceManager::DeviceErrorSetupFailed;
}
QColor newColor = action.params().first().value<QColor>();
if (!newColor.isValid()) {
return DeviceManager::DeviceErrorActionParameterError;
}
qDebug() << "executing boblight action" << newColor;
m_bobClient->setColor(device->params().value("channel").toInt(), newColor);
m_bobClient->sync();
device->setStateValue(colorStateTypeId, newColor);
return DeviceManager::DeviceErrorNoError;
}
void DevicePluginBoblight::connectToBoblight()
{
m_bobClient->connect(m_config.value("boblighthost").toString(), m_config.value("boblightport").toInt());
}

View File

@ -0,0 +1,43 @@
#ifndef DEVICEPLUGINBOBLIGHT_H
#define DEVICEPLUGINBOBLIGHT_H
#include "plugin/deviceplugin.h"
#include <QProcess>
class BobClient;
class DevicePluginBoblight : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.hiveyourhome.DevicePlugin" FILE "devicepluginboblight.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginBoblight();
QList<Vendor> supportedVendors() const override;
QList<DeviceClass> supportedDevices() const override;
DeviceManager::HardwareResources requiredHardware() const override;
bool configureAutoDevice(QList<Device *> loadedDevices, Device *device) const override;
QString pluginName() const override;
QUuid pluginId() const override;
QVariantMap configuration() const override;
void setConfiguration(const QVariantMap &configuration) override;
public slots:
DeviceManager::DeviceError executeAction(Device *device, const Action &action);
private slots:
void connectToBoblight();
private:
BobClient *m_bobClient;
QVariantMap m_config;
};
#endif // DEVICEPLUGINBOBLIGHT_H

View File

@ -0,0 +1 @@
{}

View File

@ -124,13 +124,13 @@ QUuid DevicePluginConrad::pluginId() const
return QUuid("1fd1a076-f229-4ec6-b501-48ddd15935e4");
}
void DevicePluginConrad::executeAction(Device *device, const Action &action)
DeviceManager::DeviceError DevicePluginConrad::executeAction(Device *device, const Action &action)
{
QList<int> rawData;
QByteArray binCode;
return DeviceManager::DeviceErrorNoError;
}
void DevicePluginConrad::radioData(QList<int> rawData)

View File

@ -41,7 +41,7 @@ public:
void radioData(QList<int> rawData) override;
public slots:
void executeAction(Device *device, const Action &action) override;
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
};

View File

@ -8,4 +8,6 @@ SUBDIRS += elro \
# weatherground \
openweathermap \
# boblight \
boblight {
SUBDIRS += boblight
}

View File

@ -221,7 +221,7 @@ QUuid DevicePluginElro::pluginId() const
return QUuid("2b267f81-d9ae-4f4f-89a0-7386b547cfd3");
}
void DevicePluginElro::executeAction(Device *device, const Action &action)
DeviceManager::DeviceError DevicePluginElro::executeAction(Device *device, const Action &action)
{
QList<int> rawData;
@ -314,6 +314,7 @@ void DevicePluginElro::executeAction(Device *device, const Action &action)
//qDebug() << "rawData" << rawData;
qDebug() << "transmit" << pluginName() << action.params().value("power").toBool();
transmitData(rawData);
return DeviceManager::DeviceErrorNoError;
}
void DevicePluginElro::radioData(QList<int> rawData)

View File

@ -41,7 +41,7 @@ public:
void radioData(QList<int> rawData) override;
public slots:
void executeAction(Device *device, const Action &action) override;
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
};

View File

@ -357,7 +357,7 @@ QUuid DevicePluginIntertechno::pluginId() const
return QUuid("e998d934-0397-42c1-ad63-9141bcac8563");
}
void DevicePluginIntertechno::executeAction(Device *device, const Action &action)
DeviceManager::DeviceError DevicePluginIntertechno::executeAction(Device *device, const Action &action)
{
QList<int> rawData;
@ -400,7 +400,7 @@ void DevicePluginIntertechno::executeAction(Device *device, const Action &action
}else if(familyCode == "P"){
binCode.append("01010101");
}else{
return;
return DeviceManager::DeviceErrorDeviceParameterError;
}
QString buttonCode = device->params().value("buttonCode").toString();
@ -440,7 +440,7 @@ void DevicePluginIntertechno::executeAction(Device *device, const Action &action
}else if(familyCode == "16"){
binCode.append("01010101");
}else{
return;
return DeviceManager::DeviceErrorDeviceParameterError;
}
// =======================================
@ -479,6 +479,8 @@ void DevicePluginIntertechno::executeAction(Device *device, const Action &action
qDebug() << "transmit" << pluginName() << familyCode << buttonCode << action.params().value("power").toBool();
transmitData(rawData);
return DeviceManager::DeviceErrorNoError;
}
void DevicePluginIntertechno::radioData(QList<int> rawData)

View File

@ -41,7 +41,7 @@ public:
void radioData(QList<int> rawData) override;
public slots:
void executeAction(Device *device, const Action &action) override;
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
};

View File

@ -27,6 +27,7 @@
VendorId guhVendorId = VendorId("2062d64d-3232-433c-88bc-0d33c0ba2ba6");
DeviceClassId mockDeviceClassId = DeviceClassId("753f0d32-0468-4d08-82ed-1964aab03298");
DeviceClassId mockDeviceAutoClassId = DeviceClassId("ab4257b3-7548-47ee-9bd4-7dc3004fd197");
EventTypeId mockEvent1Id = EventTypeId("45bf3752-0fc6-46b9-89fd-ffd878b5b22b");
EventTypeId mockEvent2Id = EventTypeId("863d5920-b1cf-4eb9-88bd-8f7b8583b1cf");
StateTypeId mockIntStateId = StateTypeId("80baec19-54de-4948-ac46-31eabfaceb83");
@ -103,6 +104,19 @@ QList<DeviceClass> DevicePluginMock::supportedDevices() const
ret.append(deviceClassMock);
// Auto created mock device
DeviceClass deviceClassMockAuto(pluginId(), guhVendorId, mockDeviceAutoClassId);
deviceClassMockAuto.setName("Mock Device (Auto created)");
deviceClassMockAuto.setCreateMethod(DeviceClass::CreateMethodAuto);
mockParams.clear();
deviceClassMockAuto.setParams(mockParams);
deviceClassMockAuto.setStates(mockStates);
deviceClassMockAuto.setEvents(mockEvents);
deviceClassMockAuto.setActions(mockActions);
ret.append(deviceClassMockAuto);
return ret;
}
@ -144,7 +158,24 @@ void DevicePluginMock::deviceRemoved(Device *device)
m_daemons.take(device)->deleteLater();
}
void DevicePluginMock::executeAction(Device *device, const Action &action)
bool DevicePluginMock::configureAutoDevice(QList<Device *> loadedDevices, Device *device) const
{
Q_ASSERT(device->deviceClassId() == mockDeviceAutoClassId);
qDebug() << "checking loadedDevices" << loadedDevices.count();
// We only want to have one auto mock device. So if there's already anything in loadedDevices, don't crearte a new one.
if (loadedDevices.count() > 0) {
return false;
}
device->setName("Mock Device (Auto created)");
QVariantMap params;
params.insert("httpport", 4242);
device->setParams(params);
return true;
}
DeviceManager::DeviceError DevicePluginMock::executeAction(Device *device, const Action &action)
{
qDebug() << "Should execute action" << action.actionTypeId();
m_daemons.value(device)->actionExecuted(action.actionTypeId());

View File

@ -45,8 +45,10 @@ public:
bool deviceCreated(Device *device) override;
void deviceRemoved(Device *device) override;
bool configureAutoDevice(QList<Device *> loadedDevices, Device *device) const override;
public slots:
void executeAction(Device *device, const Action &action) override;
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
private slots:
void setState(const StateTypeId &stateTypeId, const QVariant &value);

View File

@ -68,8 +68,16 @@ QVariantMap ActionHandler::ExecuteAction(const QVariantMap &params)
returns.insert("errorMessage", "No such device");
returns.insert("success", false);
break;
case DeviceManager::DeviceErrorActionTypeNotFound:
returns.insert("errorMessage", "ActionType not found");
returns.insert("success", false);
break;
case DeviceManager::DeviceErrorMissingParameter:
returns.insert("errorMessage", "Missing parameter");
returns.insert("success", false);
break;
default:
returns.insert("errorMessage", "Unknown error.");
returns.insert("errorMessage", QString("Unknown error %1").arg(error));
returns.insert("success", false);
}

View File

@ -57,13 +57,13 @@ DeviceHandler::DeviceHandler(QObject *parent) :
setReturns("GetPlugins", returns);
params.clear(); returns.clear();
setDescription("SetPluginParams", "Set a plugin's params.");
setDescription("SetPluginConfiguration", "Set a plugin's params.");
params.insert("pluginId", "uuid");
QVariantList pluginParams;
pluginParams.append(JsonTypes::paramTypeRef());
params.insert("pluginParams", pluginParams);
setParams("SetPluginParams", params);
setReturns("SetPluginParams", returns);
setParams("SetPluginConfiguration", params);
setReturns("SetPluginConfiguration", returns);
params.clear(); returns.clear();
setDescription("AddConfiguredDevice", "Add a configured device.");
@ -190,11 +190,11 @@ QVariantMap DeviceHandler::GetPlugins(const QVariantMap &params) const
return returns;
}
QVariantMap DeviceHandler::SetPluginParams(const QVariantMap &params)
QVariantMap DeviceHandler::SetPluginConfiguration(const QVariantMap &params)
{
QUuid pluginId = params.value("pluginId").toUuid();
PluginId pluginId = PluginId(params.value("pluginId").toString());
QVariantMap pluginParams = params.value("pluginParams").toMap();
GuhCore::instance()->deviceManager()->plugin(pluginId)->setConfiguration(pluginParams);
GuhCore::instance()->deviceManager()->setPluginConfig(pluginId, pluginParams);
return QVariantMap();
}
@ -223,6 +223,10 @@ QVariantMap DeviceHandler::AddConfiguredDevice(const QVariantMap &params)
returns.insert("errorMessage", "Error creating device. Device setup failed.");
returns.insert("success", false);
break;
case DeviceManager::DeviceErrorCreationNotSupported:
returns.insert("errorMessage", "Error creating device. This device can't be created this way.");
returns.insert("success", false);
break;
default:
returns.insert("errorMessage", "Unknown error.");
returns.insert("success", false);
@ -245,7 +249,7 @@ QVariantMap DeviceHandler::GetConfiguredDevices(const QVariantMap &params) const
QVariantMap DeviceHandler::RemoveConfiguredDevice(const QVariantMap &params)
{
QVariantMap returns;
switch(GuhCore::instance()->deviceManager()->removeConfiguredDevice(params.value("deviceId").toUuid())) {
switch(GuhCore::instance()->deviceManager()->removeConfiguredDevice(DeviceId(params.value("deviceId").toString()))) {
case DeviceManager::DeviceErrorNoError:
returns.insert("success", true);
returns.insert("errorMessage", "");
@ -303,7 +307,7 @@ QVariantMap DeviceHandler::GetStateValue(const QVariantMap &params) const
{
QVariantMap returns;
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(params.value("deviceId").toUuid());
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(DeviceId(params.value("deviceId").toString()));
if (!device) {
returns.insert("success", false);
returns.insert("errorMessage", "No such device");

View File

@ -35,7 +35,7 @@ public:
Q_INVOKABLE QVariantMap GetPlugins(const QVariantMap &params) const;
Q_INVOKABLE QVariantMap SetPluginParams(const QVariantMap &params);
Q_INVOKABLE QVariantMap SetPluginConfiguration(const QVariantMap &params);
Q_INVOKABLE QVariantMap AddConfiguredDevice(const QVariantMap &params);

View File

@ -29,6 +29,8 @@ QString JsonTypes::s_lastError;
QVariantList JsonTypes::s_basicTypes;
QVariantList JsonTypes::s_ruleTypes;
QVariantList JsonTypes::s_createMethodTypes;
QVariantList JsonTypes::s_setupMethodTypes;
QVariantMap JsonTypes::s_paramType;
QVariantMap JsonTypes::s_param;
@ -49,6 +51,8 @@ void JsonTypes::init()
// BasicTypes
s_basicTypes << "uuid" << "string" << "integer" << "double" << "bool";
s_ruleTypes << "RuleTypeMatchAll" << "RuleTypeMatchAny";
s_createMethodTypes << "CreateMethodUser" << "CreateMethodAuto" << "CreateMethodDiscovery";
s_setupMethodTypes << "SetupMethodJustAdd" << "SetupMethodDisplayPin" << "SetupMethodEnterPin" << "SetupMethodPushButton";
// ParamType
s_paramType.insert("name", "string");
@ -107,6 +111,8 @@ void JsonTypes::init()
s_deviceClass.insert("events", QVariantList() << eventTypeRef());
s_deviceClass.insert("actions", QVariantList() << actionTypeRef());
s_deviceClass.insert("params", QVariantList() << paramTypeRef());
s_deviceClass.insert("setupMethod", setupMethodTypesRef());
s_deviceClass.insert("createMethod", createMethodTypesRef());
// Device
s_device.insert("id", "uuid");
@ -133,6 +139,8 @@ QVariantMap JsonTypes::allTypes()
QVariantMap allTypes;
allTypes.insert("BasicType", basicTypes());
allTypes.insert("ParamType", paramTypeDescription());
allTypes.insert("CreateMethodType", createMethodTypes());
allTypes.insert("SetupMethodType", setupMethodTypes());
allTypes.insert("StateType", stateTypeDescription());
allTypes.insert("EventType", eventTypeDescription());
allTypes.insert("ActionType", actionTypeDescription());
@ -224,6 +232,8 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass)
variant.insert("states", stateTypes);
variant.insert("events", eventTypes);
variant.insert("actions", actionTypes);
variant.insert("createMethod", s_createMethodTypes.at(deviceClass.createMethod()));
variant.insert("setupMethod", s_setupMethodTypes.at(deviceClass.setupMethod()));
return variant;
}
@ -406,6 +416,18 @@ QPair<bool, QString> JsonTypes::validateVariant(const QVariant &templateVariant,
qDebug() << "value not allowed in" << ruleTypesRef();
return result;
}
} else if (refName == createMethodTypesRef()) {
QPair<bool, QString> result = validateCreateMethodType(variant);
if (!result.first) {
qDebug() << "value not allowed in" << createMethodTypesRef();
return result;
}
} else if (refName == setupMethodTypesRef()) {
QPair<bool, QString> result = validateSetupMethodType(variant);
if (!result.first) {
qDebug() << "value not allowed in" << createMethodTypesRef();
return result;
}
} else {
qDebug() << "unhandled ref:" << refName;
return report(false, QString("Unhandled ref %1. Server implementation incomplete.").arg(refName));
@ -464,3 +486,13 @@ QPair<bool, QString> JsonTypes::validateRuleType(const QVariant &variant)
{
return report(s_ruleTypes.contains(variant.toString()), QString("Unknown rules type %1").arg(variant.toString()));
}
QPair<bool, QString> JsonTypes::validateCreateMethodType(const QVariant &variant)
{
return report(s_createMethodTypes.contains(variant.toString()), QString("Unknwon createMethod type %1").arg(variant.toString()));
}
QPair<bool, QString> JsonTypes::validateSetupMethodType(const QVariant &variant)
{
return report(s_setupMethodTypes.contains(variant.toString()), QString("Unknwon setupMethod type %1").arg(variant.toString()));
}

View File

@ -63,6 +63,8 @@ public:
DECLARE_TYPE(basicTypes, "BasicType")
DECLARE_TYPE(ruleTypes, "RuleType")
DECLARE_TYPE(createMethodTypes, "CreateMethodType")
DECLARE_TYPE(setupMethodTypes, "SetupMethodType")
DECLARE_OBJECT(paramType, "ParamType")
DECLARE_OBJECT(param, "Param")
DECLARE_OBJECT(stateType, "StateType")
@ -94,6 +96,8 @@ public:
static QPair<bool, QString> validateVariant(const QVariant &templateVariant, const QVariant &variant);
static QPair<bool, QString> validateBasicType(const QVariant &variant);
static QPair<bool, QString> validateRuleType(const QVariant &variant);
static QPair<bool, QString> validateCreateMethodType(const QVariant &variant);
static QPair<bool, QString> validateSetupMethodType(const QVariant &variant);
private:
static bool s_initialized;

View File

@ -29,6 +29,7 @@ Q_IMPORT_PLUGIN(DevicePluginConrad)
Q_IMPORT_PLUGIN(DevicePluginMock)
//Q_IMPORT_PLUGIN(DevicePluginWeatherground)
Q_IMPORT_PLUGIN(DevicePluginOpenweathermap)
Q_IMPORT_PLUGIN(DevicePluginBoblight)
int main(int argc, char *argv[])
{

View File

@ -156,9 +156,6 @@ RuleEngine::RuleError RuleEngine::addRule(const Event &event, const QList<Action
/*! Add a new \l{Rule} with the given \a event, \a states and \a actions to the engine. */
RuleEngine::RuleError RuleEngine::addRule(const Event &event, const QList<State> &states, const QList<Action> &actions)
{
qDebug() << "adding rule: Event:" << event.eventTypeId() << "with" << actions.count() << "actions";
DeviceClass eventDeviceClass = GuhCore::instance()->deviceManager()->findDeviceClassforEvent(event.eventTypeId());
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(event.deviceId());
if (!device) {
qWarning() << "Cannot create rule. No configured device for eventTypeId" << event.eventTypeId();

View File

@ -23,3 +23,7 @@ LIBS += -L../plugins/deviceplugins/conrad -lguh_devicepluginconrad
LIBS += -L../plugins/deviceplugins/mock -lguh_devicepluginmock
#LIBS += -L../plugins/deviceplugins/weatherground -lguh_devicepluginweatherground
LIBS += -L../plugins/deviceplugins/openweathermap -lguh_devicepluginopenweathermap
boblight {
LIBS += -L../plugins/deviceplugins/boblight -lguh_devicepluginboblight -L/usr/local/lib/ -lboblight
}

View File

@ -34,6 +34,7 @@ int mockDevice2Port = 7331;
extern VendorId guhVendorId;
extern DeviceClassId mockDeviceClassId;
extern DeviceClassId mockDeviceAutoClassId;
extern ActionTypeId mockAction1Id;
extern EventTypeId mockEvent1Id;
extern StateTypeId mockIntStateId;
@ -53,6 +54,10 @@ private slots:
void getSupportedDevices_data();
void getSupportedDevices();
void addConfiguredDevice_data();
void addConfiguredDevice();
void getConfiguredDevices();
void executeAction_data();
@ -87,6 +92,10 @@ private:
void TestJSONRPC::initTestcase()
{
// If testcase asserts cleanup won't do. Lets clear any previous test run settings leftovers
QSettings settings;
settings.clear();
QCoreApplication::instance()->setOrganizationName("guh-test");
m_commandId = 0;
@ -119,7 +128,7 @@ void TestJSONRPC::initTestcase()
void TestJSONRPC::cleanupTestCase()
{
QSettings settings;
settings.clear();
// settings.clear();
}
QVariant TestJSONRPC::injectAndWait(const QString &method, const QVariantMap &params = QVariantMap())
@ -241,8 +250,8 @@ void TestJSONRPC::getSupportedDevices_data()
QTest::addColumn<VendorId>("vendorId");
QTest::addColumn<int>("resultCount");
QTest::newRow("vendor guh") << guhVendorId << 1;
QTest::newRow("no filter") << VendorId() << 1;
QTest::newRow("vendor guh") << guhVendorId << 2;
QTest::newRow("no filter") << VendorId() << 2;
QTest::newRow("invalid vendor") << VendorId("93e7d361-8025-4354-b17e-b68406c800bc") << 0;
}
@ -261,7 +270,43 @@ void TestJSONRPC::getSupportedDevices()
QCOMPARE(supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().count(), resultCount);
if (resultCount > 0) {
QString deviceName = supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().first().toMap().value("name").toString();
QCOMPARE(deviceName, QString("Mock Device"));
QVERIFY(deviceName.startsWith(QString("Mock Device")));
}
}
void TestJSONRPC::addConfiguredDevice_data()
{
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<QVariantMap>("deviceParams");
QTest::addColumn<bool>("success");
QVariantMap deviceParams;
deviceParams.insert("httpport", mockDevice1Port - 1);
QTest::newRow("User, JustAdd") << mockDeviceClassId << deviceParams << true;
QTest::newRow("Auto, JustAdd") << mockDeviceAutoClassId << deviceParams << false;
}
void TestJSONRPC::addConfiguredDevice()
{
QFETCH(DeviceClassId, deviceClassId);
QFETCH(QVariantMap, deviceParams);
QFETCH(bool, success);
QVariantMap params;
params.insert("deviceClassId", deviceClassId);
params.insert("deviceParams", deviceParams);
QVariant response = injectAndWait("Devices.AddConfiguredDevice", params);
qDebug() << "response is" << response;
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), success);
if (success) {
QUuid deviceId(response.toMap().value("params").toMap().value("deviceId").toString());
params.clear();
params.insert("deviceId", deviceId.toString());
injectAndWait("Devices.RemoveConfiguredDevice", params);
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
}
}
@ -270,7 +315,7 @@ void TestJSONRPC::getConfiguredDevices()
QVariant response = injectAndWait("Devices.GetConfiguredDevices");
QVariantList devices = response.toMap().value("params").toMap().value("devices").toList();
QCOMPARE(devices.count(), 1);
QCOMPARE(devices.count(), 2); // There should be one auto created mock device and the one created in initTestcase()
}
void TestJSONRPC::executeAction_data()
@ -478,6 +523,10 @@ void TestJSONRPC::stateChangeEmitsNotifications()
void TestJSONRPC::removeDevice()
{
QVERIFY(!m_mockDeviceId.isNull());
QSettings settings;
settings.beginGroup(m_mockDeviceId.toString());
// Make sure we have some config values for this device
QVERIFY(settings.allKeys().count() > 0);
QVariantMap params;
params.insert("deviceId", m_mockDeviceId);
@ -487,7 +536,6 @@ void TestJSONRPC::removeDevice()
QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true);
// Make sure the device is gone from settings too
QSettings settings;
QCOMPARE(settings.allKeys().count(), 0);
}

View File

@ -1,7 +1,11 @@
#!/bin/bash
if [ -z $4 ]; then
echo "usage: $0 host actionTypeId deviceId power"
if [ -z $3 ]; then
echo "usage: $0 host actionTypeId deviceId [paramname paramvalue]"
elif [ -z $4 ]; then
(echo '{"id":1, "method":"Actions.ExecuteAction","params":{"actionTypeId": "{'$2'}", "deviceId":"{'$3'}"}'; sleep 1) | nc $1 1234
elif [ -z $5 ]; then
echo "usage: $0 host actionTypeId deviceId [paramname paramvalue]"
else
(echo '{"id":1, "method":"Actions.ExecuteAction","params":{"actionTypeId": "{'$2'}", "deviceId":"{'$3'}","params":{"power":"'$4'"}}}'; sleep 1) | nc $1 1234
(echo '{"id":1, "method":"Actions.ExecuteAction","params":{"actionTypeId": "{'$2'}", "deviceId":"{'$3'}","params":{"'$4'":"'$5'"}}}'; sleep 1) | nc $1 1234
fi

View File

@ -1,7 +0,0 @@
#!/bin/bash
if [ -z $3 ]; then
echo "usage: $0 host deviceId stateTypeId"
else
(echo '{"id":1, "method":"Devices.GetDeviceState", "params":{"deviceId":"'$2'", "stateTypeId":"'$3'"}}'; sleep 1) | nc $1 1234
fi

7
tests/scripts/getstatevalue.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
if [ -z $3 ]; then
echo "usage: $0 host deviceId stateTypeId"
else
(echo '{"id":1, "method":"Devices.GetStateValue", "params":{"deviceId":"'$2'", "stateTypeId":"'$3'"}}'; sleep 1) | nc $1 1234
fi

View File

@ -2,6 +2,8 @@
if [ -z $1 ]; then
echo "usage $0 host"
else
elif [ -z $2 ]; then
(echo '{"id":1, "method":"Devices.GetSupportedDevices"}'; sleep 1) | nc $1 1234
else
(echo '{"id":1, "method":"Devices.GetSupportedDevices", "params":{"vendorId":"'$2'"}}'; sleep 1) | nc $1 1234
fi

View File

@ -1,7 +1,11 @@
#!/bin/bash
if [ -z $1 ]; then
echo "usage $0 host"
if [ -z $4 ]; then
echo "usage $0 host pluginid param1name param1value [param2name param2value]"
elif [ -z $5 ]; then
(echo '{"id":1, "method":"Devices.SetPluginConfiguration", "params":{"pluginId":"'$2'", "pluginParams":{"'$3'":"'$4'"}}}'; sleep 1) | nc $1 1234
elif [ -z $6 ]; then
echo "usage $0 host pluginid param1name param1value [param2name param2value]"
else
(echo '{"id":1, "method":"Devices.SetPluginConfig", "params":{"pluginId":"'$2'", "params":{"foo":"bar"}}}'; sleep 1) | nc $1 1234
(echo '{"id":1, "method":"Devices.SetPluginConfiguration", "params":{"pluginId":"'$2'", "pluginParams":{"'$3'":"'$4'", "'$5'":"'$6'"}}}'; sleep 1) | nc $1 1234
fi