add initial support for pairing devices (Only CreateMethodDiscovery and SetupMethodPushButton so far)

implement an initial Philips Hue plugin to test it
add a python script to test stuff
pull/135/head
Michael Zanetti 2014-06-19 22:29:45 +02:00
parent 3bb19915bf
commit a6725ec4e9
25 changed files with 1169 additions and 14 deletions

View File

@ -95,6 +95,9 @@ DeviceManager::DeviceManager(QObject *parent) :
QObject(parent),
m_radio433(0)
{
qRegisterMetaType<DeviceClassId>();
qRegisterMetaType<DeviceDescriptor>();
m_pluginTimer.setInterval(15000);
connect(&m_pluginTimer, &QTimer::timeout, this, &DeviceManager::timerEvent);
@ -228,7 +231,7 @@ QPair<DeviceManager::DeviceError, QString> DeviceManager::addConfiguredDevice(co
return qMakePair<DeviceError, QString>(DeviceErrorCreationMethodNotSupported, "CreateMethodDiscovery");
}
DeviceDescriptor descriptor = m_discoveredDevices.take(deviceClassId).take(deviceDescriptorId);
DeviceDescriptor descriptor = m_discoveredDevices.take(deviceDescriptorId);
if (!descriptor.isValid()) {
return qMakePair<DeviceError>(DeviceErrorDeviceDescriptorNotFound, deviceDescriptorId.toString());
}
@ -236,6 +239,98 @@ QPair<DeviceManager::DeviceError, QString> DeviceManager::addConfiguredDevice(co
return addConfiguredDeviceInternal(deviceClassId, descriptor.params(), deviceId);
}
QPair<DeviceManager::DeviceError, QString> DeviceManager::pairDevice(const DeviceClassId &deviceClassId, const QList<Param> &params)
{
DeviceClass deviceClass = findDeviceClass(deviceClassId);
if (deviceClass.id().isNull()) {
qWarning() << "cannot find a device class with id" << deviceClassId;
return qMakePair<DeviceError, QString>(DeviceErrorDeviceClassNotFound, deviceClassId.toString());
}
if (deviceClass.setupMethod() == DeviceClass::SetupMethodJustAdd) {
qWarning() << "Cannot setup this device this way. No need to pair this device.";
return qMakePair<DeviceError, QString>(DeviceErrorCreationMethodNotSupported, "No need to pair this device.");
}
QUuid pairingTransactionId = QUuid::createUuid();
m_pairingsJustAdd.insert(pairingTransactionId, qMakePair<DeviceClassId, QList<Param> >(deviceClassId, params));
if (deviceClass.setupMethod() == DeviceClass::SetupMethodDisplayPin) {
// TODO: fetch PIN from device plugin
qWarning() << "SetupMethodDisplayPin not implemented yet";
return qMakePair<DeviceError, QString>(DeviceErrorSetupFailed, "SetupMethodDisplayPin Not implemented yet.");
}
return qMakePair<DeviceError, QString>(DeviceErrorNoError, pairingTransactionId.toString());
}
QPair<DeviceManager::DeviceError, QString> DeviceManager::pairDevice(const DeviceClassId &deviceClassId, const DeviceDescriptorId &deviceDescriptorId)
{
DeviceClass deviceClass = findDeviceClass(deviceClassId);
if (deviceClass.id().isNull()) {
qWarning() << "cannot find a device class with id" << deviceClassId;
return qMakePair<DeviceError, QString>(DeviceErrorDeviceClassNotFound, deviceClassId.toString());
}
if (deviceClass.setupMethod() == DeviceClass::SetupMethodJustAdd) {
qWarning() << "Cannot setup this device this way. No need to pair this device.";
return qMakePair<DeviceError, QString>(DeviceErrorCreationMethodNotSupported, "No need to pair this device.");
}
if (!m_discoveredDevices.contains(deviceDescriptorId)) {
qWarning() << "Cannot find a DeviceDescriptor with ID" << deviceClassId.toString();
return qMakePair<DeviceError, QString>(DeviceErrorDeviceDescriptorNotFound, deviceDescriptorId.toString());
}
QUuid pairingTransactionId = QUuid::createUuid();
m_pairingsDiscovery.insert(pairingTransactionId, qMakePair<DeviceClassId, DeviceDescriptorId>(deviceClassId, deviceDescriptorId));
if (deviceClass.setupMethod() == DeviceClass::SetupMethodDisplayPin) {
// TODO: fetch PIN from device plugin
qWarning() << "SetupMethodDisplayPin not implemented yet";
return qMakePair<DeviceError, QString>(DeviceErrorSetupFailed, "SetupMethodDisplayPin Not implemented yet.");
}
return qMakePair<DeviceError, QString>(DeviceErrorNoError, pairingTransactionId.toString());
}
QPair<DeviceManager::DeviceError, QString> DeviceManager::confirmPairing(const QUuid &pairingTransactionId, const QString &secret)
{
if (m_pairingsJustAdd.contains(pairingTransactionId)) {
qWarning() << "this SetupMethod is not implemented yet";
m_pairingsJustAdd.remove(pairingTransactionId);
return qMakePair<DeviceError, QString>(DeviceErrorSetupFailed, "Not implemented yet");
}
if (m_pairingsDiscovery.contains(pairingTransactionId)) {
DeviceDescriptorId deviceDescriptorId = m_pairingsDiscovery.value(pairingTransactionId).second;
DeviceClassId deviceClassId = m_pairingsDiscovery.value(pairingTransactionId).first;
DeviceDescriptor deviceDescriptor = m_discoveredDevices.value(deviceDescriptorId);
DevicePlugin *plugin = m_devicePlugins.value(m_supportedDevices.value(deviceClassId).pluginId());
if (!plugin) {
qWarning() << "Can't find a plugin for this device class";
return report(DeviceErrorPluginNotFound, m_supportedDevices.value(deviceClassId).pluginId().toString());
}
QPair<DeviceSetupStatus, QString> status = plugin->confirmPairing(pairingTransactionId, deviceClassId, deviceDescriptor.params());
switch (status.first) {
case DeviceSetupStatusSuccess:
m_pairingsDiscovery.remove(pairingTransactionId);
return report(DeviceErrorNoError);
case DeviceSetupStatusFailure:
m_pairingsDiscovery.remove(pairingTransactionId);
return report(DeviceErrorSetupFailed, status.second);
case DeviceSetupStatusAsync:
return report(DeviceErrorAsync);
}
}
return report(DeviceErrorPairingTransactionIdNotFound, pairingTransactionId.toString());
}
QPair<DeviceManager::DeviceError, QString> DeviceManager::addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QList<Param> &params, const DeviceId id)
{
DeviceClass deviceClass = findDeviceClass(deviceClassId);
@ -244,6 +339,11 @@ QPair<DeviceManager::DeviceError, QString> DeviceManager::addConfiguredDeviceInt
return qMakePair<DeviceError, QString>(DeviceErrorDeviceClassNotFound, deviceClassId.toString());
}
if (deviceClass.setupMethod() != DeviceClass::SetupMethodJustAdd) {
qWarning() << "Cannot setup this device this way. You need to pair this device.";
return qMakePair<DeviceError, QString>(DeviceErrorCreationMethodNotSupported, "You need to pair this device.");
}
QPair<DeviceError, QString> result = verifyParams(deviceClass.paramTypes(), params);
if (result.first != DeviceErrorNoError) {
return result;
@ -437,6 +537,7 @@ void DeviceManager::loadPlugins()
connect(pluginIface, &DevicePlugin::devicesDiscovered, this, &DeviceManager::slotDevicesDiscovered);
connect(pluginIface, &DevicePlugin::deviceSetupFinished, this, &DeviceManager::slotDeviceSetupFinished);
connect(pluginIface, &DevicePlugin::actionExecutionFinished, this, &DeviceManager::actionExecutionFinished);
connect(pluginIface, &DevicePlugin::pairingFinished, this, &DeviceManager::slotPairingFinished);
}
}
}
@ -527,11 +628,9 @@ void DeviceManager::createNewAutoDevices()
void DeviceManager::slotDevicesDiscovered(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> deviceDescriptors)
{
QHash<DeviceDescriptorId, DeviceDescriptor> descriptorHash;
foreach (const DeviceDescriptor &descriptor, deviceDescriptors) {
descriptorHash.insert(descriptor.id(), descriptor);
m_discoveredDevices.insert(descriptor.id(), descriptor);
}
m_discoveredDevices[deviceClassId] = descriptorHash;
emit devicesDiscovered(deviceClassId, deviceDescriptors);
}
@ -589,6 +688,78 @@ void DeviceManager::slotDeviceSetupFinished(Device *device, DeviceManager::Devic
emit deviceSetupFinished(device, DeviceManager::DeviceErrorNoError, QString());
}
void DeviceManager::slotPairingFinished(const QUuid &pairingTransactionId, DeviceManager::DeviceSetupStatus status, const QString &errorMessage)
{
if (!m_pairingsJustAdd.contains(pairingTransactionId) && !m_pairingsDiscovery.contains(pairingTransactionId)) {
DevicePlugin *plugin = dynamic_cast<DevicePlugin*>(sender());
if (plugin) {
qWarning() << "Received a pairing finished without waiting for it from plugin:" << plugin->metaObject()->className();
} else {
qWarning() << "Received a pairing finished without waiting for it.";
}
return;
}
DeviceClassId deviceClassId;
QList<Param> params;
// Do this before checking status to make sure we clean up our hashes properly
if (m_pairingsJustAdd.contains(pairingTransactionId)) {
QPair<DeviceClassId, QList<Param> > pair = m_pairingsJustAdd.take(pairingTransactionId);
deviceClassId = pair.first;
params = pair.second;
}
if (m_pairingsDiscovery.contains(pairingTransactionId)) {
QPair<DeviceClassId, DeviceDescriptorId> pair = m_pairingsDiscovery.take(pairingTransactionId);
DeviceDescriptorId deviceDescriptorId = pair.second;
DeviceDescriptor descriptor = m_discoveredDevices.take(deviceDescriptorId);
deviceClassId = pair.first;
params = descriptor.params();
}
qDebug() << "pairingfinsihed!" << errorMessage;
if (status != DeviceSetupStatusSuccess) {
qDebug() << "emitting shit";
emit pairingFinished(pairingTransactionId, DeviceErrorSetupFailed, errorMessage);
return;
}
DeviceClass deviceClass = findDeviceClass(deviceClassId);
DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId());
if (!plugin) {
qWarning() << "Cannot find a plugin for this device class!";
emit pairingFinished(pairingTransactionId, DeviceErrorPluginNotFound, deviceClass.pluginId().toString());
return;
}
DeviceId id = DeviceId::createDeviceId();
Device *device = new Device(plugin->pluginId(), id, deviceClassId, this);
device->setName(deviceClass.name());
device->setParams(params);
QPair<DeviceSetupStatus, QString> setupStatus = setupDevice(device);
switch (setupStatus.first) {
case DeviceSetupStatusFailure:
qWarning() << "Device setup failed. Not adding device to system.";
delete device;
emit pairingFinished(pairingTransactionId, DeviceErrorSetupFailed, QString("Device setup failed: %1").arg(setupStatus.second));
return;
case DeviceSetupStatusAsync:
case DeviceSetupStatusSuccess:
qDebug() << "Device setup complete.";
break;
}
m_configuredDevices.append(device);
storeConfiguredDevices();
emit pairingFinished(pairingTransactionId, DeviceErrorNoError, QString(), id);
}
void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value)
{
Device *device = qobject_cast<Device*>(sender());

View File

@ -59,7 +59,8 @@ public:
DeviceErrorCreationMethodNotSupported,
DeviceErrorActionParameterError,
DeviceErrorDeviceDescriptorNotFound,
DeviceErrorAsync
DeviceErrorAsync,
DeviceErrorPairingTransactionIdNotFound,
};
enum DeviceSetupStatus {
@ -82,6 +83,9 @@ public:
QList<Device*> configuredDevices() const;
QPair<DeviceError, QString> addConfiguredDevice(const DeviceClassId &deviceClassId, const QList<Param> &params, const DeviceId id = DeviceId::createDeviceId());
QPair<DeviceError, QString> addConfiguredDevice(const DeviceClassId &deviceClassId, const DeviceDescriptorId &deviceDescriptorId, const DeviceId &id = DeviceId::createDeviceId());
QPair<DeviceError, QString> pairDevice(const DeviceClassId &deviceClassId, const QList<Param> &params);
QPair<DeviceError, QString> pairDevice(const DeviceClassId &deviceClassId, const DeviceDescriptorId &deviceDescriptorId);
QPair<DeviceError, QString> confirmPairing(const QUuid &pairingTransactionId, const QString &secret = QString());
QPair<DeviceError, QString> removeConfiguredDevice(const DeviceId &deviceId);
Device* findConfiguredDevice(const DeviceId &id) const;
@ -94,6 +98,7 @@ signals:
void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value);
void devicesDiscovered(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &devices);
void deviceSetupFinished(Device *device, DeviceError status, const QString &errorMessage);
void pairingFinished(const QUuid &pairingTransactionId, DeviceError status, const QString &errorMessage, const DeviceId &deviceId = DeviceId());
void actionExecutionFinished(const ActionId, DeviceError status, const QString &errorMessage);
public slots:
@ -106,6 +111,7 @@ private slots:
void createNewAutoDevices();
void slotDevicesDiscovered(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> deviceDescriptors);
void slotDeviceSetupFinished(Device *device, DeviceManager::DeviceSetupStatus status, const QString &errorMessage);
void slotPairingFinished(const QUuid &pairingTransactionId, DeviceManager::DeviceSetupStatus status, const QString &errorMessage);
// Only connect this to Devices. It will query the sender()
void slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value);
@ -128,7 +134,7 @@ private:
QHash<VendorId, QList<DeviceClassId> > m_vendorDeviceMap;
QHash<DeviceClassId, DeviceClass> m_supportedDevices;
QList<Device*> m_configuredDevices;
QHash<DeviceClassId, QHash<DeviceDescriptorId, DeviceDescriptor> > m_discoveredDevices;
QHash<DeviceDescriptorId, DeviceDescriptor> m_discoveredDevices;
QHash<PluginId, DevicePlugin*> m_devicePlugins;
@ -139,6 +145,9 @@ private:
QTimer m_pluginTimer;
QList<Device*> m_pluginTimerUsers;
QHash<QUuid, QPair<DeviceClassId, QList<Param> > > m_pairingsJustAdd;
QHash<QUuid, QPair<DeviceClassId, DeviceDescriptorId> > m_pairingsDiscovery;
friend class DevicePlugin;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(DeviceManager::HardwareResources)

View File

@ -189,6 +189,16 @@ void DeviceClass::setSetupMethod(DeviceClass::SetupMethod setupMethod)
m_setupMethod = setupMethod;
}
QString DeviceClass::pairingInfo() const
{
return m_pairingInfo;
}
void DeviceClass::setPairingInfo(const QString &pairingInfo)
{
m_pairingInfo = pairingInfo;
}
/*! 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

@ -74,6 +74,9 @@ public:
SetupMethod setupMethod() const;
void setSetupMethod(SetupMethod setupMethod);
QString pairingInfo() const;
void setPairingInfo(const QString &pairingInfo);
bool operator==(const DeviceClass &device) const;
private:
@ -89,6 +92,7 @@ private:
QList<ParamType> m_discoveryParamTypes;
CreateMethod m_createMethod;
SetupMethod m_setupMethod;
QString m_pairingInfo;
};
#endif

View File

@ -53,4 +53,6 @@ private:
QList<Param> m_params;
};
Q_DECLARE_METATYPE(DeviceDescriptor)
#endif // DEVICEDESCRIPTION_H

View File

@ -147,6 +147,11 @@ bool DevicePlugin::configureAutoDevice(QList<Device*> loadedDevices, Device *dev
return false;
}
/*! Reimplement this if you support a DeviceClass with createMethod CreateMethodDiscovery.
This will be called to discover Devices for the given DeviceClass. This will always
be an async operation. Return DeviceErrorAsync or DeviceErrorNoError if the discovery
has been started successfully. Return an appropriate error otherwise.
Once devices are discovered, emit devicesDiscovered() once. */
DeviceManager::DeviceError DevicePlugin::discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap &params) const
{
Q_UNUSED(deviceClassId)
@ -177,6 +182,15 @@ void DevicePlugin::deviceRemoved(Device *device)
Q_UNUSED(device)
}
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePlugin::confirmPairing(const QUuid &pairingTransactionId, const DeviceClassId &deviceClassId, const QList<Param> &params)
{
Q_UNUSED(pairingTransactionId)
Q_UNUSED(deviceClassId)
Q_UNUSED(params)
return reportDeviceSetup(DeviceManager::DeviceSetupStatusFailure, "Plugin does not implement pairing.");
}
QList<ParamType> DevicePlugin::configurationDescription() const
{
return QList<ParamType>();

View File

@ -55,6 +55,8 @@ public:
virtual QPair<DeviceManager::DeviceSetupStatus, QString> setupDevice(Device *device);
virtual void deviceRemoved(Device *device);
virtual QPair<DeviceManager::DeviceSetupStatus, QString> confirmPairing(const QUuid &pairingTransactionId, const DeviceClassId &deviceClassId, const QList<Param> &params);
// Hardware input
virtual void radioData(QList<int> rawData) {Q_UNUSED(rawData)}
virtual void guhTimer() {}
@ -76,6 +78,7 @@ signals:
void emitEvent(const Event &event);
void devicesDiscovered(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors);
void deviceSetupFinished(Device *device, DeviceManager::DeviceSetupStatus status, const QString &errorMessage);
void pairingFinished(const QUuid &pairingTransactionId, DeviceManager::DeviceSetupStatus status, const QString &errorMessage);
void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status, const QString &errorMessage);
void configValueChanged(const QString &paramName, const QVariant &value);

View File

@ -9,6 +9,7 @@ SUBDIRS += elro \
lircd \
wakeonlan \
mailnotification \
philipshue \
boblight {
SUBDIRS += boblight

View File

@ -428,10 +428,10 @@ DeviceManager::DeviceError DevicePluginOpenweathermap::discoverDevices(const Dev
qDebug() << "should discover divces for" << deviceClassId << params.value("location").toString();
if(params.value("location").toString() == ""){
m_openweaher->searchAutodetect();
return DeviceManager::DeviceErrorNoError;
return DeviceManager::DeviceErrorAsync;
}
m_openweaher->search(params.value("location").toString());
return DeviceManager::DeviceErrorNoError;
return DeviceManager::DeviceErrorAsync;
}
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginOpenweathermap::setupDevice(Device *device)

View File

@ -0,0 +1,242 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "devicepluginphilipshue.h"
#include "plugin/device.h"
#include "devicemanager.h"
#include "types/param.h"
#include "huebridgeconnection.h"
#include <QDebug>
#include <QStringList>
#include <QJsonDocument>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QColor>
VendorId hueVendorId = VendorId("0ae1e001-2aa6-47ed-b8c0-334c3728a68f");
PluginId huePluginUuid = PluginId("5f2e634b-b7f3-48ee-976a-b5ae22aa5c55");
DeviceClassId hueDeviceClassId = DeviceClassId("d8f4c397-e05e-47c1-8917-8e72d4d0d47c");
StateTypeId hueColorStateTypeId = StateTypeId("d25423e7-b924-4b20-80b6-77eecc65d089");
ActionTypeId setHueColorActionTypeId = ActionTypeId("29cc299a-818b-47b2-817f-c5a6361545e4");
DevicePluginPhilipsHue::DevicePluginPhilipsHue():
m_discovery(new Discovery(this))
{
connect(m_discovery, &Discovery::discoveryDone, this, &DevicePluginPhilipsHue::discoveryDone);
m_nam = new QNetworkAccessManager(this);
}
QList<Vendor> DevicePluginPhilipsHue::supportedVendors() const
{
QList<Vendor> ret;
Vendor philips(hueVendorId, "Philips");
ret.append(philips);
return ret;
}
QList<DeviceClass> DevicePluginPhilipsHue::supportedDevices() const
{
QList<DeviceClass> ret;
DeviceClass deviceClassHue(pluginId(), hueVendorId, hueDeviceClassId);
deviceClassHue.setName("Hue");
deviceClassHue.setCreateMethod(DeviceClass::CreateMethodDiscovery);
deviceClassHue.setSetupMethod(DeviceClass::SetupMethodPushButton);
deviceClassHue.setPairingInfo("Please press the button on the Hue bridge and then press OK");
QList<ParamType> paramTypes;
ParamType ipParam("ip", QVariant::String);
paramTypes.append(ipParam);
deviceClassHue.setParamTypes(paramTypes);
QList<StateType> hueStates;
StateType colorState(hueColorStateTypeId);
colorState.setName("color");
colorState.setType(QVariant::Color);
colorState.setDefaultValue(QColor(Qt::black));
hueStates.append(colorState);
deviceClassHue.setStateTypes(hueStates);
QList<ActionType> hueActons;
ActionType setColorAction(setHueColorActionTypeId);
setColorAction.setName("Set color");
QList<ParamType> actionParamsSetColor;
ParamType actionParamSetColor("color", QVariant::Color);
actionParamsSetColor.append(actionParamSetColor);
setColorAction.setParameters(actionParamsSetColor);
hueActons.append(setColorAction);
deviceClassHue.setActions(hueActons);
ret.append(deviceClassHue);
return ret;
}
DeviceManager::HardwareResources DevicePluginPhilipsHue::requiredHardware() const
{
return DeviceManager::HardwareResourceNone;
}
bool DevicePluginPhilipsHue::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));
// QList<Param> params;
// Param param("channel");
// param.setValue(index);
// params.append(param);
// device->setParams(params);
// device->setStateValue(colorStateTypeId, m_bobClient->currentColor(index));
// return true;
// }
return false;
}
QString DevicePluginPhilipsHue::pluginName() const
{
return "Philips Hue";
}
PluginId DevicePluginPhilipsHue::pluginId() const
{
return huePluginUuid;
}
QList<ParamType> DevicePluginPhilipsHue::configurationDescription() const
{
QList<ParamType> params;
return params;
}
DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap &params) const
{
m_discovery->findBridges(4000);
return DeviceManager::DeviceErrorAsync;
}
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::confirmPairing(const QUuid &pairingTransactionId, const DeviceClassId &deviceClassId, const QList<Param> &params)
{
Param ipParam;
foreach (const Param &param, params) {
if (param.name() == "ip") {
ipParam = param;
}
}
if (!ipParam.isValid()) {
return reportDeviceSetup(DeviceManager::DeviceSetupStatusFailure, "Missing parameter: ip");
}
QString username = "guh-" + QUuid::createUuid().toString().remove(QRegExp("[\\{\\}]*")).remove(QRegExp("\\-[0-9a-f\\-]*"));
QVariantMap createUserParams;
createUserParams.insert("devicetype", "guh");
createUserParams.insert("username", username);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(createUserParams);
QByteArray data = jsonDoc.toJson();
QNetworkRequest request(QUrl("http://" + ipParam.value().toString() + "/api"));
QNetworkReply *reply = m_nam->post(request, data);
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::createUserFinished);
HueBridgeConnection *bridge = new HueBridgeConnection(QHostAddress(ipParam.value().toString()), username);
m_pairings.insert(reply, qMakePair<QUuid, HueBridgeConnection*>(pairingTransactionId, bridge));
return reportDeviceSetup(DeviceManager::DeviceSetupStatusAsync);
}
QPair<DeviceManager::DeviceError, QString> DevicePluginPhilipsHue::executeAction(Device *device, const Action &action)
{
// if (!m_bobClient->connected()) {
return report(DeviceManager::DeviceErrorSetupFailed, device->id().toString());
// }
// QColor newColor = action.param("color").value().value<QColor>();
// if (!newColor.isValid()) {
// return report(DeviceManager::DeviceErrorActionParameterError, "color");
// }
// qDebug() << "executing boblight action" << newColor;
// m_bobClient->setColor(device->paramValue("channel").toInt(), newColor);
// m_bobClient->sync();
// device->setStateValue(colorStateTypeId, newColor);
// return report();
}
void DevicePluginPhilipsHue::discoveryDone(const QList<QHostAddress> &bridges)
{
qDebug() << "discovered bridges" << bridges.count();
QList<DeviceDescriptor> deviceDescriptors;
foreach (const QHostAddress &bridge, bridges) {
DeviceDescriptor descriptor(hueDeviceClassId, "Philips Hue bridge", bridge.toString());
QList<Param> params;
Param param("ip", bridge.toString());
params.append(param);
descriptor.setParams(params);
deviceDescriptors.append(descriptor);
}
emit devicesDiscovered(hueDeviceClassId, deviceDescriptors);
}
void DevicePluginPhilipsHue::createUserFinished()
{
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
QByteArray data = reply->readAll();
QPair<QUuid, HueBridgeConnection*> pair = m_pairings.take(reply);
QUuid pairingTransactionId = pair.first;
HueBridgeConnection *bridge = pair.second;
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
emit pairingFinished(pairingTransactionId, DeviceManager::DeviceSetupStatusFailure, "Pairing failed. Failed to parse response from Hue Bridge.");
delete bridge;
return;
}
QVariantMap response = jsonDoc.toVariant().toList().first().toMap();
if (response.contains("error")) {
qDebug() << "Failed to pair Hue bridge:" << response.value("error").toMap().value("description");
emit pairingFinished(pairingTransactionId, DeviceManager::DeviceSetupStatusFailure, "Pairing failed:" + response.value("error").toMap().value("description").toString());
delete bridge;
return;
}
emit pairingFinished(pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess, QString());
m_bridges.append(bridge);
qDebug() << "response" << response << data;
}

View File

@ -0,0 +1,71 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICEPLUGINPHILIPSHUE_H
#define DEVICEPLUGINPHILIPSHUE_H
#include "plugin/deviceplugin.h"
#include "discovery.h"
#include "huebridgeconnection.h"
class QNetworkAccessManager;
class QNetworkReply;
class DevicePluginPhilipsHue: public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginphilipshue.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginPhilipsHue();
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;
PluginId pluginId() const override;
QList<ParamType> configurationDescription() const override;
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const QVariantMap &params) const override;
QPair<DeviceManager::DeviceSetupStatus, QString> confirmPairing(const QUuid &pairingTransactionId, const DeviceClassId &deviceClassId, const QList<Param> &params) override;
public slots:
QPair<DeviceManager::DeviceError, QString> executeAction(Device *device, const Action &action);
private slots:
void discoveryDone(const QList<QHostAddress> &bridges);
void createUserFinished();
private:
QList<Param> m_config;
Discovery *m_discovery;
QNetworkAccessManager *m_nam;
QHash<QNetworkReply*, QPair<QUuid, HueBridgeConnection*> > m_pairings;
QList<HueBridgeConnection*> m_bridges;
};
#endif // DEVICEPLUGINBOBLIGHT_H

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2013 Christian Muehlhaeuser
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Christian Muehlhaeuser <muesli@gmail.com>
*/
#include "discovery.h"
#include <QDebug>
#include <QTimer>
Discovery::Discovery(QObject *parent) :
QUdpSocket(parent),
m_timeout(new QTimer(this))
{
quint16 port = 1900;
unsigned int tries = 0;
const unsigned int maxtries = 10;
while (!bind(port++)) {
if (++tries == maxtries) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection);
return;
}
}
connect(this, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
m_timeout->setSingleShot(true);
connect(m_timeout, SIGNAL(timeout()), this, SLOT(onTimeout()));
}
bool Discovery::findBridges(int timeout)
{
m_timeout->stop();
m_reportedBridges.clear();
QString b("M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"MAN: \"ssdp:discover\"\r\n"
"MX: %1\r\n"
"ST: libhue:idl\r\n");
b.arg(timeout / 1000);
// qDebug() << "writing datagram" << b;
m_timeout->start(timeout);
if (writeDatagram(b.toUtf8(), QHostAddress("239.255.255.250"), 1900) < 0) {
return false;
}
return true;
}
void Discovery::onTimeout()
{
emit discoveryDone(m_reportedBridges);
}
void Discovery::onReadyRead()
{
while (hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
// qDebug() << "got datagram" << datagram;
if (!m_reportedBridges.contains(sender)) {
m_reportedBridges << sender;
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2013 Christian Muehlhaeuser
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Christian Muehlhaeuser <muesli@gmail.com>
* Michael Zanetti <michael_zanetti@gmx.net>
*/
#ifndef DISCOVERY_H
#define DISCOVERY_H
#include <QUdpSocket>
#include <QHostAddress>
class QTimer;
class Discovery: public QUdpSocket
{
Q_OBJECT
public:
Discovery(QObject *parent);
bool findBridges(int timeout);
signals:
void error();
void discoveryDone(QList<QHostAddress> bridges);
private slots:
void onTimeout();
void onReadyRead();
private:
QList<QHostAddress> m_reportedBridges;
QTimer *m_timeout;
};
#endif

View File

@ -0,0 +1,39 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "huebridgeconnection.h"
#include <QUuid>
HueBridgeConnection::HueBridgeConnection(const QHostAddress &address, const QString &username, QObject *parent) :
QObject(parent),
m_address(address),
m_username(username)
{
}
QString HueBridgeConnection::username() const
{
return m_username;
}
QHostAddress HueBridgeConnection::address() const
{
return m_address;
}

View File

@ -0,0 +1,39 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef HUEBRIDGECONNECTION_H
#define HUEBRIDGECONNECTION_H
#include <QObject>
#include <QHostAddress>
class HueBridgeConnection : public QObject
{
Q_OBJECT
public:
explicit HueBridgeConnection(const QHostAddress &address, const QString &username = QString(), QObject *parent = 0);
QHostAddress address() const;
QString username() const;
private:
QHostAddress m_address;
QString m_username;
};
#endif // HUEBRIDGECONNECTION_H

View File

@ -0,0 +1,18 @@
include(../../plugins.pri)
TARGET = $$qtLibraryTarget(guh_devicepluginphilipshue)
QT += network
SOURCES += \
devicepluginphilipshue.cpp \
discovery.cpp \
huebridgeconnection.cpp
HEADERS += \
devicepluginphilipshue.h \
discovery.h \
huebridgeconnection.h

View File

@ -77,7 +77,12 @@ DeviceHandler::DeviceHandler(QObject *parent) :
setReturns("SetPluginConfiguration", returns);
params.clear(); returns.clear();
setDescription("AddConfiguredDevice", "Add a configured device. Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class.");
setDescription("AddConfiguredDevice", "Add a configured device with a setupMethod of SetupMethodJustAdd. "
"For devices with a setupMethod different than SetupMethodJustAdd, use PairDevice. "
"Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class. "
"CreateMethodJustAdd takes the parameters you want to have with that device. "
"CreateMethodDiscovery requires the use of a deviceDescriptorId."
);
params.insert("deviceClassId", "uuid");
params.insert("o:deviceDescriptorId", "uuid");
QVariantList deviceParams;
@ -89,6 +94,34 @@ DeviceHandler::DeviceHandler(QObject *parent) :
returns.insert("o:deviceId", "uuid");
setReturns("AddConfiguredDevice", returns);
returns.clear(); // Reused params from above!
setDescription("PairDevice", "Pair a device. "
"Use this for DeviceClasses with a setupMethod different than SetupMethodJustAdd."
"Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class. "
"CreateMethodJustAdd takes the parameters you want to have with that device. "
"CreateMethodDiscovery requires the use of a deviceDescriptorId. "
"If success is true, the return values will contain a pairingTransactionId, a displayMessage and "
"the setupMethod. Depending on the setupMethod you should either proceed with AddConfiguredDevice "
" or PairDevice."
);
setParams("PairDevice", params);
returns.insert("success", "bool");
returns.insert("errorMessage", "string");
returns.insert("o:pairingTransactionId", "uuid");
returns.insert("o:displayMessage", "string");
returns.insert("o:setupMethod", JsonTypes::setupMethodTypesRef());
setReturns("PairDevice", returns);
params.clear(); returns.clear();
setDescription("ConfirmPairing", "Confirm an ongoing pairing. In case of SetupMethodEnterPin also provide the pin in the params.");
params.insert("pairingTransactionId", "uuid");
params.insert("o:secret", "string");
setParams("ConfirmPairing", params);
returns.insert("success", "bool");
returns.insert("errorMessage", "string");
returns.insert("o:deviceId", "uuid");
setReturns("ConfirmPairing", returns);
params.clear(); returns.clear();
setDescription("GetConfiguredDevices", "Returns a list of configured devices.");
setParams("GetConfiguredDevices", params);
@ -165,8 +198,9 @@ DeviceHandler::DeviceHandler(QObject *parent) :
setParams("StateChanged", params);
connect(GuhCore::instance()->deviceManager(), &DeviceManager::deviceStateChanged, this, &DeviceHandler::deviceStateChanged);
connect(GuhCore::instance()->deviceManager(), &DeviceManager::devicesDiscovered, this, &DeviceHandler::devicesDiscovered);
connect(GuhCore::instance()->deviceManager(), &DeviceManager::devicesDiscovered, this, &DeviceHandler::devicesDiscovered, Qt::QueuedConnection);
connect(GuhCore::instance()->deviceManager(), &DeviceManager::deviceSetupFinished, this, &DeviceHandler::deviceSetupFinished);
connect(GuhCore::instance()->deviceManager(), &DeviceManager::pairingFinished, this, &DeviceHandler::pairingFinished);
}
QString DeviceHandler::name() const
@ -211,6 +245,7 @@ JsonReply *DeviceHandler::GetDiscoveredDevices(const QVariantMap &params) const
DeviceManager::DeviceError status = GuhCore::instance()->deviceManager()->discoverDevices(deviceClassId, params.value("discoveryParams").toMap());
switch (status) {
case DeviceManager::DeviceErrorAsync:
case DeviceManager::DeviceErrorNoError: {
JsonReply *reply = createAsyncReply("GetDiscoveredDevices");
m_discoverRequests.insert(deviceClassId, reply);
@ -352,6 +387,79 @@ JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap &params)
return createReply(returns);
}
JsonReply *DeviceHandler::PairDevice(const QVariantMap &params)
{
DeviceClassId deviceClassId(params.value("deviceClassId").toString());
DeviceClass deviceClass = GuhCore::instance()->deviceManager()->findDeviceClass(deviceClassId);
QPair<DeviceManager::DeviceError, QString> status;
if (params.contains("deviceDescriptorId")) {
DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
status = GuhCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceDescriptorId);
} else {
QList<Param> deviceParams;
foreach (const QString &paramName, params.value("deviceParams").toMap().keys()) {
Param param(paramName);
param.setValue(params.value("deviceParams").toMap().value(paramName));
deviceParams.append(param);
}
status = GuhCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceParams);
}
QVariantMap returns;
switch (status.first) {
case DeviceManager::DeviceErrorNoError:
returns.insert("success", true);
returns.insert("errorMessage", "");
returns.insert("displayMessage", deviceClass.pairingInfo());
returns.insert("pairingTransactionId", status.second);
returns.insert("setupMethod", JsonTypes::setupMethodTypes().at(deviceClass.setupMethod()));
break;
case DeviceManager::DeviceErrorDeviceClassNotFound:
returns.insert("errorMessage", QString("Error pairing device. Device class not found: %1").arg(status.second));
returns.insert("success", false);
break;
case DeviceManager::DeviceErrorDeviceDescriptorNotFound:
returns.insert("errorMessage", QString("Error pairing device. Device descriptor not found: %1").arg(status.second));
returns.insert("success", false);
break;
case DeviceManager::DeviceErrorCreationMethodNotSupported:
returns.insert("errorMessage", QString("Error pairing device. This device can't be created this way: %1").arg(status.second));
returns.insert("success", false);
break;
case DeviceManager::DeviceErrorPairingTransactionIdNotFound:
returns.insert("errorMessage", QString("Error pairing device. PairingTransactionId not found: %1").arg(status.second));
returns.insert("success", false);
break;
}
return createReply(returns);
}
JsonReply *DeviceHandler::ConfirmPairing(const QVariantMap &params)
{
QUuid pairingTransactionId = params.value("pairingTransactionId").toUuid();
QString secret = params.value("secret").toString();
QPair<DeviceManager::DeviceError, QString> status = GuhCore::instance()->deviceManager()->confirmPairing(pairingTransactionId, secret);
JsonReply *reply = 0;
QVariantMap returns;
switch (status.first) {
case DeviceManager::DeviceErrorAsync:
reply = createAsyncReply("ConfirmPairing");
m_asyncPairingRequests.insert(pairingTransactionId, reply);
return reply;
case DeviceManager::DeviceErrorNoError:
returns.insert("success", true);
returns.insert("errorMessage", QString());
case DeviceManager::DeviceErrorSetupFailed:
default:
returns.insert("success", false);
returns.insert("errorMessage", status.second);
}
reply = createReply(returns);
return reply;
}
JsonReply* DeviceHandler::GetConfiguredDevices(const QVariantMap &params) const
{
Q_UNUSED(params)
@ -503,3 +611,24 @@ void DeviceHandler::deviceSetupFinished(Device *device, DeviceManager::DeviceErr
reply->setData(returns);
reply->finished();
}
void DeviceHandler::pairingFinished(const QUuid &pairingTransactionId, DeviceManager::DeviceError status, const QString &errorMessage, const DeviceId &deviceId)
{
qDebug() << "handler: pairing finished";
JsonReply *reply = m_asyncPairingRequests.take(pairingTransactionId);
if (!reply) {
qDebug() << "not for me";
return;
}
QVariantMap returns;
if (status == DeviceManager::DeviceErrorNoError) {
returns.insert("success", true);
returns.insert("errorMessage", QString());
returns.insert("deviceId", deviceId.toString());
} else {
returns.insert("success", false);
returns.insert("errorMessage", errorMessage);
}
reply->setData(returns);
reply->finished();
}

View File

@ -44,6 +44,10 @@ public:
Q_INVOKABLE JsonReply* AddConfiguredDevice(const QVariantMap &params);
Q_INVOKABLE JsonReply* PairDevice(const QVariantMap &params);
Q_INVOKABLE JsonReply* ConfirmPairing(const QVariantMap &params);
Q_INVOKABLE JsonReply* GetConfiguredDevices(const QVariantMap &params) const;
Q_INVOKABLE JsonReply* RemoveConfiguredDevice(const QVariantMap &params);
@ -66,10 +70,13 @@ private slots:
void deviceSetupFinished(Device *device, DeviceManager::DeviceError status);
void pairingFinished(const QUuid &pairingTransactionId, DeviceManager::DeviceError status, const QString &errorMessage, const DeviceId &deviceId);
private:
// A cache for async replies
mutable QHash<DeviceClassId, JsonReply*> m_discoverRequests;
mutable QHash<DeviceId, JsonReply*> m_asynDeviceAdditions;
mutable QHash<QUuid, JsonReply*> m_asyncPairingRequests;
};
#endif // DEVICEHANDLER_H

View File

@ -42,7 +42,7 @@
#include <QJsonDocument>
#include <QStringList>
#define JSON_PROTOCOL_VERSION 1
#define JSON_PROTOCOL_VERSION 2
JsonRPCServer::JsonRPCServer(QObject *parent):
JsonHandler(parent),

View File

@ -31,6 +31,7 @@ Q_IMPORT_PLUGIN(DevicePluginOpenweathermap)
Q_IMPORT_PLUGIN(DevicePluginLircd)
Q_IMPORT_PLUGIN(DevicePluginWakeOnLan)
Q_IMPORT_PLUGIN(DevicePluginMailNotification)
Q_IMPORT_PLUGIN(DevicePluginPhilipsHue)
#if USE_BOBLIGHT
Q_IMPORT_PLUGIN(DevicePluginBoblight)

View File

@ -144,7 +144,10 @@ RuleEngine::RuleEngine(QObject *parent) :
list of all \l{Action}{Actions} that should be executed. */
QList<Action> RuleEngine::evaluateEvent(const Event &event)
{
qDebug() << "got event:" << event;
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(event.deviceId());
qDebug() << "got event:" << event << device->name();
QList<Action> actions;
for (int i = 0; i < m_rules.count(); ++i) {
qDebug() << "evaluating rule" << i << m_rules.at(i).eventDescriptors();

View File

@ -29,6 +29,7 @@ LIBS += -L../plugins/deviceplugins/openweathermap -lguh_devicepluginopenweatherm
LIBS += -L../plugins/deviceplugins/lircd -lguh_devicepluginlircd
LIBS += -L../plugins/deviceplugins/mailnotification -lguh_devicepluginmailnotification
LIBS += -L../plugins/deviceplugins/wakeonlan -lguh_devicepluginwakeonlan
LIBS += -L../plugins/deviceplugins/philipshue -lguh_devicepluginphilipshue
boblight {
xcompile {

View File

@ -1,4 +1,4 @@
1
2
{
"methods": {
"Actions.ExecuteAction": {
@ -33,7 +33,7 @@
}
},
"Devices.AddConfiguredDevice": {
"description": "Add a configured device. Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class.",
"description": "Add a configured device with a setupMethod of SetupMethodJustAdd. For devices with a setupMethod different than SetupMethodJustAdd, use PairDevice. Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class. CreateMethodJustAdd takes the parameters you want to have with that device. CreateMethodDiscovery requires the use of a deviceDescriptorId.",
"params": {
"deviceClassId": "uuid",
"o:deviceDescriptorId": "uuid",
@ -47,6 +47,18 @@
"success": "bool"
}
},
"Devices.ConfirmPairing": {
"description": "Confirm an ongoing pairing. In case of SetupMethodEnterPin also provide the pin in the params.",
"params": {
"o:secret": "string",
"pairingTransactionId": "uuid"
},
"returns": {
"errorMessage": "string",
"o:deviceId": "uuid",
"success": "bool"
}
},
"Devices.GetActionTypes": {
"description": "Get action types for a specified deviceClassId.",
"params": {
@ -162,6 +174,23 @@
]
}
},
"Devices.PairDevice": {
"description": "Pair a device. Use this for DeviceClasses with a setupMethod different than SetupMethodJustAdd.Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class. CreateMethodJustAdd takes the parameters you want to have with that device. CreateMethodDiscovery requires the use of a deviceDescriptorId. If success is true, the return values will contain a pairingTransactionId, a displayMessage and the setupMethod. Depending on the setupMethod you should either proceed with AddConfiguredDevice or PairDevice.",
"params": {
"deviceClassId": "uuid",
"o:deviceDescriptorId": "uuid",
"o:deviceParams": [
"$ref:Param"
]
},
"returns": {
"errorMessage": "string",
"o:displayMessage": "string",
"o:pairingTransactionId": "uuid",
"o:setupMethod": "$ref:SetupMethodType",
"success": "bool"
}
},
"Devices.RemoveConfiguredDevice": {
"description": "Remove a device from the system.",
"params": {

225
tests/scripts/cmdmgr.py Executable file
View File

@ -0,0 +1,225 @@
#!/usr/bin/python
import telnetlib
import json
HOST='localhost'
PORT=1234
commandId=0
methods = {'List supported Vendors': 'list_vendors',
'List supported Devices': 'list_deviceClasses',
'List configured Devices': 'list_configured_devices',
'Add Device': 'add_device',
'Remove a device': 'remove_device',
'List supported Devices by vendor': 'list_deviceClasses_by_vendor' }
def get_selection(title, options):
print "\n\n", title
for i in range(0,len(options)):
print "%i: %s" % (i, options[i])
selection = raw_input("Enter selection: ")
return int(selection)
def send_command(method, params = None):
global commandId
if params == None or len(params) == 0:
command = '{"id": %i, "method":"%s"}\n' % (commandId, method)
else:
paramString = ""
for i in range(0, len(params)):
if paramString != "":
paramString = paramString + ", "
paramKey = params.keys()[i]
paramString = paramString + ('"%s":"%s"' % (paramKey, params[paramKey].encode('utf-8')))
command = '{"id": %i, "method":"%s", "params":{%s}}\n' % (commandId, method, paramString)
commandId = commandId + 1
tn.write(command)
response = json.loads(tn.read_until("\n}\n"))
if response['status'] != "success":
print "JSON error happened: %s" % response
return response
def get_vendors():
return send_command("Devices.GetSupportedVendors")
def list_vendors():
response = get_vendors();
print "=== Vendors ==="
for vendor in response['params']['vendors']:
print "%40s %s" % (vendor['name'], vendor['id'])
print "=== Vendors ==="
def select_vendor():
vendors = get_vendors()['params']['vendors']
vendorList = []
vendorIdList = []
for i in range(0,len(vendors)):
vendorList.append(vendors[i]['name'])
vendorIdList.append(vendors[i]['id'])
selection = get_selection("Please select vendor", vendorList)
return vendorIdList[selection]
def get_deviceClasses(vendorId = None):
params = {};
if vendorId != None:
params['vendorId'] = vendorId
return send_command("Devices.GetSupportedDevices", params)['params']['deviceClasses']
def list_deviceClasses(vendorId = None):
response = get_deviceClasses(vendorId)
print "=== Devices ==="
for deviceClass in response:
print "%40s %s" % (deviceClass['name'], deviceClass['id'])
print "=== Devices ==="
def select_deviceClass():
vendorId = select_vendor()
deviceClasses = get_deviceClasses(vendorId)
if len(deviceClasses) == 0:
print "No supported devices for this vendor"
return ""
deviceClassList = []
deviceClassIdList = []
for i in range(0,len(deviceClasses)):
deviceClassList.append(deviceClasses[i]['name'])
deviceClassIdList.append(deviceClasses[i]['id'])
selection = get_selection("Please select device class", deviceClassList)
return deviceClassIdList[selection]
def list_deviceClasses_by_vendor():
vendorId = select_vendor()
list_deviceClasses(vendorId)
def get_configured_devices():
return send_command("Devices.GetConfiguredDevices")['params']['devices']
def list_configured_devices():
deviceList = get_configured_devices()
print "=== Configured Devices ==="
for device in deviceList:
print "Name: %40s, ID: %s, DeviceClassID: %s" % (device['name'], device['id'], device['deviceClassId'])
print "=== Configured Devices ==="
def discover_device(deviceClassId = None):
if deviceClassId == None:
deviceClassId = select_deviceClass()
params = {}
params['deviceClassId'] = deviceClassId
print "\ndiscovering..."
response = send_command("Devices.GetDiscoveredDevices", params)
deviceDescriptorList = [];
deviceDescriptorIdList = [];
for deviceDescriptor in response['params']['deviceDescriptors']:
deviceDescriptorList.append("%s (%s)" % (deviceDescriptor['title'], deviceDescriptor['description']))
deviceDescriptorIdList.append(deviceDescriptor['id'])
selection = get_selection("Please select a device descriptor", deviceDescriptorList)
return deviceDescriptorIdList[selection]
def get_deviceClass(deviceClassId):
deviceClasses = get_deviceClasses()
for deviceClass in deviceClasses:
# print "got deviceclass", deviceClass
if deviceClass['id'] == deviceClassId:
return deviceClass
return None
def add_configured_device(deviceClassId):
params = {}
params['deviceClassId'] = deviceClassId
response = send_command("Devices.AddConfiguredDevice", params)
if response['params']['success'] != "true":
print "Error executing method: %s" % response['params']['errorMessage']
return
print "Added device: %s" % response['params']['deviceId']
def add_discovered_device(deviceClassId, deviceDescriptorId):
params = {}
params['deviceClassId'] = deviceClassId
params['deviceDescriptorId'] = deviceDescriptorId
deviceClass = get_deviceClass(deviceClassId)
if deviceClass['setupMethod'] == "SetupMethodJustAdd":
response = send_command("Devices.AddConfiguredDevice", params)
if not response['params']['success']:
print "Adding device failed: %s" % response['params']['errorMessage']
else:
print "Device added successfully. Device ID: %s" % response['params']['deviceId']
else:
params = {}
params['deviceClassId'] = deviceClassId
params['deviceDescriptorId'] = deviceDescriptorId
response = send_command("Devices.PairDevice", params)
print "pairdevice response:", response
if not response['params']['success']:
print "Pairing failed: %s", response['params']['errorMessage']
return
else:
print "\nPairing device %s\n\n%s" % (deviceClass['name'], response['params']['displayMessage'])
if response['params']['setupMethod'] == "SetupMethodPushButton":
raw_input("Press enter to confirm")
params = {}
params['pairingTransactionId'] = response['params']['pairingTransactionId']
response = send_command("Devices.ConfirmPairing", params)
if response['params']['success']:
success = True
print "Device paired successfully"
else:
print "Error pairing device: %s" % response['params']['errorMessage']
def add_device():
deviceClassId = select_deviceClass()
if deviceClassId == "":
print "Empty deviceClass. Can't continue"
return
deviceClass = get_deviceClass(deviceClassId)
print "createmethod is", deviceClass['createMethod']
if deviceClass['createMethod'] == "CreateMethodUser":
add_configured_device(deviceClassId)
elif deviceClass['createMethod'] == "CreateMethodDiscovery":
deviceDescriptorId = discover_device(deviceClassId)
add_discovered_device(deviceClassId, deviceDescriptorId)
elif deviceClass['createMethod'] == "CreateMethodAuto":
print "Can't create this device manually. It'll be created automatically when hardware is discovered."
def select_device():
devices = get_configured_devices()
deviceList = []
deviceIdList = []
for i in range(len(devices)):
deviceList.append(devices[i]['name'])
deviceIdList.append(devices[i]['id'])
selection = get_selection("Please select a device", deviceList)
return deviceIdList[selection]
def remove_device():
deviceId = select_device()
print "should remove device", deviceId
params = {}
params['deviceId'] = deviceId
response = send_command("Devices.RemoveConfiguredDevice", params)
if response['params']['success']:
print "Successfully deleted device"
else:
print "Error deleting device %s" % deviceId
tn = telnetlib.Telnet(HOST, PORT)
packet = tn.read_until("\n}\n")
packet = json.loads(packet)
print "connected to", packet["server"], "\nserver version:", packet["version"], "\nprotocol version:", packet["protocol version"], "\n"
while True:
selection = get_selection("What do you want to do?", methods.keys())
selectionKey = methods.keys()
methodName = methods[methods.keys()[selection]]
methodToCall = globals()[methods[methods.keys()[selection]]]
methodToCall()