more work on the Hue plugin

This commit is contained in:
Michael Zanetti 2014-06-22 00:04:30 +02:00
parent 5a0d5fb7c2
commit fcdf3e1e0b
11 changed files with 863 additions and 85 deletions

View File

@ -456,6 +456,7 @@ DeviceClass DeviceManager::findDeviceClass(const DeviceClassId &deviceClassId) c
its \l{DevicePlugin}. Then will dispatch the execution to the \l{DevicePlugin}.*/
QPair<DeviceManager::DeviceError, QString> DeviceManager::executeAction(const Action &action)
{
qDebug() << "should execute action";
foreach (Device *device, m_configuredDevices) {
if (action.deviceId() == device->id()) {
// found device
@ -464,6 +465,7 @@ QPair<DeviceManager::DeviceError, QString> DeviceManager::executeAction(const Ac
DeviceClass deviceClass = findDeviceClass(device->deviceClassId());
bool found = false;
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
qDebug() << "checking" << actionType.id() << action.actionTypeId();
if (actionType.id() == action.actionTypeId()) {
QPair<DeviceError, QString> paramCheck = verifyParams(actionType.parameters(), action.params());
if (paramCheck.first != DeviceErrorNoError) {
@ -736,7 +738,12 @@ void DeviceManager::slotPairingFinished(const QUuid &pairingTransactionId, Devic
return;
}
// Ok... pairing went fine... Let consumers know about it and inform them about the ongoing setup with a deviceId.
DeviceId id = DeviceId::createDeviceId();
emit pairingFinished(pairingTransactionId, DeviceErrorNoError, QString(), id);
QList<DeviceId> newDevices;
QString setupErrorMessage;
Device *device = new Device(plugin->pluginId(), id, deviceClassId, this);
device->setName(deviceClass.name());
device->setParams(params);
@ -745,19 +752,22 @@ void DeviceManager::slotPairingFinished(const QUuid &pairingTransactionId, Devic
switch (setupStatus.first) {
case DeviceSetupStatusFailure:
qWarning() << "Device setup failed. Not adding device to system.";
setupErrorMessage = setupStatus.second;
emit deviceSetupFinished(device, DeviceError::DeviceErrorSetupFailed, QString("Device setup failed: %1").arg(errorMessage));
delete device;
emit pairingFinished(pairingTransactionId, DeviceErrorSetupFailed, QString("Device setup failed: %1").arg(setupStatus.second));
return;
break;
case DeviceSetupStatusAsync:
return;
case DeviceSetupStatusSuccess:
qDebug() << "Device setup complete.";
newDevices.append(id);
break;
}
m_configuredDevices.append(device);
storeConfiguredDevices();
emit pairingFinished(pairingTransactionId, DeviceErrorNoError, QString(), id);
emit deviceSetupFinished(device, DeviceError::DeviceErrorNoError, QString());
}
void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value)
@ -842,6 +852,7 @@ QPair<DeviceManager::DeviceSetupStatus,QString> DeviceManager::setupDevice(Devic
QPair<DeviceManager::DeviceError, QString> DeviceManager::verifyParams(const QList<ParamType> paramTypes, const QList<Param> &params, bool requireAll)
{
foreach (const Param &param, params) {
qDebug() << "verifying param" << param.name() << paramTypes;
QPair<DeviceManager::DeviceError, QString> result = verifyParam(paramTypes, param);
if (result.first != DeviceErrorNoError) {
return result;

View File

@ -81,6 +81,7 @@ signals:
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);
void autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors);
protected:
DeviceManager *deviceManager() const;

View File

@ -25,10 +25,6 @@
#include <QDebug>
#include <QStringList>
#include <QJsonDocument>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QColor>
VendorId hueVendorId = VendorId("0ae1e001-2aa6-47ed-b8c0-334c3728a68f");
@ -37,14 +33,19 @@ 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");
ActionTypeId hueSetColorActionTypeId = ActionTypeId("29cc299a-818b-47b2-817f-c5a6361545e4");
StateTypeId huePowerStateTypeId = StateTypeId("6ac64eee-f356-4ae4-bc85-8c1244d12b02");
ActionTypeId hueSetPowerActionTypeId = ActionTypeId("7782d91e-d73a-4321-8828-da768e2f6827");
DevicePluginPhilipsHue::DevicePluginPhilipsHue():
m_discovery(new Discovery(this))
{
connect(m_discovery, &Discovery::discoveryDone, this, &DevicePluginPhilipsHue::discoveryDone);
m_nam = new QNetworkAccessManager(this);
m_bridge = new HueBridgeConnection(this);
connect(m_bridge, &HueBridgeConnection::createUserFinished, this, &DevicePluginPhilipsHue::createUserFinished);
connect(m_bridge, &HueBridgeConnection::getFinished, this, &DevicePluginPhilipsHue::getFinished);
}
QList<Vendor> DevicePluginPhilipsHue::supportedVendors() const
@ -71,6 +72,8 @@ QList<DeviceClass> DevicePluginPhilipsHue::supportedDevices() const
paramTypes.append(ipParam);
ParamType usernameParam("username", QVariant::String);
paramTypes.append(usernameParam);
ParamType numberParam("number", QVariant::Int, -1);
paramTypes.append(numberParam);
deviceClassHue.setParamTypes(paramTypes);
QList<StateType> hueStates;
@ -81,20 +84,32 @@ QList<DeviceClass> DevicePluginPhilipsHue::supportedDevices() const
colorState.setDefaultValue(QColor(Qt::black));
hueStates.append(colorState);
StateType powerState(huePowerStateTypeId);
powerState.setName("power");
powerState.setType(QVariant::Bool);
powerState.setDefaultValue(false);
hueStates.append(powerState);
deviceClassHue.setStateTypes(hueStates);
QList<ActionType> hueActons;
ActionType setColorAction(setHueColorActionTypeId);
ActionType setColorAction(hueSetColorActionTypeId);
setColorAction.setName("Set color");
QList<ParamType> actionParamsSetColor;
ParamType actionParamSetColor("color", QVariant::Color);
actionParamsSetColor.append(actionParamSetColor);
setColorAction.setParameters(actionParamsSetColor);
hueActons.append(setColorAction);
ActionType setPowerAction(hueSetPowerActionTypeId);
setPowerAction.setName("Power");
QList<ParamType> actionParamsSetPower;
ParamType actionParamSetPower("power", QVariant::Bool);
actionParamsSetPower.append(actionParamSetPower);
setPowerAction.setParameters(actionParamsSetPower);
hueActons.append(setPowerAction);
deviceClassHue.setActions(hueActons);
ret.append(deviceClassHue);
@ -151,7 +166,30 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceC
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::setupDevice(Device *device)
{
qDebug() << "setupDevice" << device->params();
return reportDeviceSetup();
Light *light = nullptr;
// Lets see if this a a newly added device... In which case its hue id number is not set, well, -1...
if (device->paramValue("number").toInt() == -1) {
if (m_unconfiguredLights.count() > 0) {
light = m_unconfiguredLights.takeFirst();
device->setParamValue("number", light->id());
} else {
// this shouldn't ever happen
return reportDeviceSetup(DeviceManager::DeviceSetupStatusFailure, "Device not configured yet and no discovered devices around.");
}
} else {
// In this case it most likely comes from the config. Just read all values from there...
light = new Light(QHostAddress(device->paramValue("ip").toString()), device->paramValue("username").toString(), device->paramValue("number").toInt());
}
connect(light, &Light::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged);
light->refresh();
m_lights.insert(light, device);
m_asyncSetups.insert(light, device);
return reportDeviceSetup(DeviceManager::DeviceSetupStatusAsync);
}
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::confirmPairing(const QUuid &pairingTransactionId, const DeviceClassId &deviceClassId, const QList<Param> &params)
@ -175,36 +213,30 @@ QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::confirm
return reportDeviceSetup(DeviceManager::DeviceSetupStatusFailure, "Missing parameter: username");
}
QVariantMap createUserParams;
createUserParams.insert("devicetype", "guh");
createUserParams.insert("username", usernameParam.value().toString());
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);
m_pairings.insert(reply, pairingTransactionId);
int id = m_bridge->createUser(QHostAddress(ipParam.value().toString()), usernameParam.value().toString());
PairingInfo pi;
pi.pairingTransactionId = pairingTransactionId;
pi.ipParam = ipParam;
pi.usernameParam = usernameParam;
m_pairings.insert(id, pi);
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();
qDebug() << "Should execute action in hue plugin";
// device->setStateValue(colorStateTypeId, newColor);
// return report();
Light *light = m_lights.key(device);
if (!light) {
return report(DeviceManager::DeviceErrorDeviceNotFound, device->id().toString());
}
if (action.actionTypeId() == hueSetColorActionTypeId) {
light->setColor(action.param("color").value().value<QColor>());
} else if (action.actionTypeId() == hueSetPowerActionTypeId) {
light->setOn(action.param("power").value().toBool());
}
return report();
}
void DevicePluginPhilipsHue::discoveryDone(const QList<QHostAddress> &bridges)
@ -218,6 +250,8 @@ void DevicePluginPhilipsHue::discoveryDone(const QList<QHostAddress> &bridges)
params.append(param);
Param userParam("username", "guh-" + QUuid::createUuid().toString().remove(QRegExp("[\\{\\}]*")).remove(QRegExp("\\-[0-9a-f\\-]*")));
params.append(userParam);
Param numberParam("number", -1);
params.append(numberParam);
descriptor.setParams(params);
deviceDescriptors.append(descriptor);
}
@ -225,28 +259,69 @@ void DevicePluginPhilipsHue::discoveryDone(const QList<QHostAddress> &bridges)
emit devicesDiscovered(hueDeviceClassId, deviceDescriptors);
}
void DevicePluginPhilipsHue::createUserFinished()
void DevicePluginPhilipsHue::createUserFinished(int id, const QVariantMap &response)
{
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
QByteArray data = reply->readAll();
QUuid pairingTransactionId = m_pairings.take(reply);
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.");
return;
}
QVariantMap response = jsonDoc.toVariant().toList().first().toMap();
qDebug() << "createuser response" << response;
PairingInfo pairingInfo = m_pairings.take(id);
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());
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure, "Pairing failed:" + response.value("error").toMap().value("description").toString());
return;
}
emit pairingFinished(pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess, QString());
qDebug() << "response" << response << data;
// Paired successfully, check how many lightbulbs there are
int getLightsId = m_bridge->get(QHostAddress(pairingInfo.ipParam.value().toString()), pairingInfo.usernameParam.value().toString(), "lights", this, "getLightsFinished");
m_pairings.insert(getLightsId, pairingInfo);
}
void DevicePluginPhilipsHue::getLightsFinished(int id, const QVariantMap &params)
{
qDebug() << "getlightsfinished" << params;
PairingInfo pairingInfo = m_pairings.take(id);
if (params.count() == 0) {
qWarning() << "No light bulbs found on this hue bridge... Cannot proceed with pairing.";
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure, "No light bulbs found on this Hue bridge.");
return;
}
// Store a list of all known Lights
foreach (const QString &lightId, params.keys()) {
Light *light = new Light(QHostAddress(pairingInfo.ipParam.value().toString()), pairingInfo.usernameParam.value().toString(), lightId.toInt(), this);
m_unconfiguredLights.insert(lightId.toInt(), light);
}
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess, QString());
// // If we have more than one device on that bridge, tell DeviceManager that there are more.
// if (params.count() > 1) {
// emit autoDevicesAppeared();
// }
}
void DevicePluginPhilipsHue::getFinished(int id, const QVariantMap &params)
{
qDebug() << "got lights" << params;
}
void DevicePluginPhilipsHue::lightStateChanged()
{
Light *light = static_cast<Light*>(sender());
Device *device;
if (m_asyncSetups.contains(light)) {
device = m_asyncSetups.take(light);
device->setName(light->name());
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess, QString());
} else {
device = m_lights.value(light);
}
if (!device) {
return;
}
device->setStateValue(hueColorStateTypeId, QVariant::fromValue(light->color()));
device->setStateValue(huePowerStateTypeId, light->on());
}

View File

@ -22,8 +22,8 @@
#include "plugin/deviceplugin.h"
#include "discovery.h"
#include "huebridgeconnection.h"
#include "light.h"
class QNetworkAccessManager;
class QNetworkReply;
class DevicePluginPhilipsHue: public DevicePlugin
@ -58,14 +58,30 @@ public slots:
private slots:
void discoveryDone(const QList<QHostAddress> &bridges);
void createUserFinished();
void createUserFinished(int id, const QVariantMap &params);
void getLightsFinished(int id, const QVariantMap &params);
void getFinished(int id, const QVariantMap &params);
void lightStateChanged();
private:
QList<Param> m_config;
Discovery *m_discovery;
QNetworkAccessManager *m_nam;
QHash<QNetworkReply*, QUuid> m_pairings;
class PairingInfo {
public:
QUuid pairingTransactionId;
Param ipParam;
Param usernameParam;
};
QHash<int, PairingInfo> m_pairings;
HueBridgeConnection *m_bridge;
QList<Light*> m_unconfiguredLights;
QHash<Light*, Device*> m_lights;
QHash<Light*, Device*> m_asyncSetups;
};
#endif // DEVICEPLUGINBOBLIGHT_H

View File

@ -18,22 +18,118 @@
#include "huebridgeconnection.h"
#include <QUuid>
#include <QJsonDocument>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
HueBridgeConnection::HueBridgeConnection(const QHostAddress &address, const QString &username, QObject *parent) :
QObject(parent),
m_address(address),
m_username(username)
HueBridgeConnection::HueBridgeConnection(QObject *parent) :
QObject(parent)
{
m_nam = new QNetworkAccessManager(this);
}
QString HueBridgeConnection::username() const
int HueBridgeConnection::createUser(const QHostAddress &address, const QString &username)
{
return m_username;
QVariantMap createUserParams;
createUserParams.insert("devicetype", "guh");
createUserParams.insert("username", username);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(createUserParams);
QByteArray data = jsonDoc.toJson();
QNetworkRequest request(QUrl("http://" + address.toString() + "/api"));
QNetworkReply *reply = m_nam->post(request, data);
connect(reply, &QNetworkReply::finished, this, &HueBridgeConnection::slotCreateUserFinished);
m_createUserMap.insert(reply, m_requestCounter);
return m_requestCounter++;
}
QHostAddress HueBridgeConnection::address() const
int HueBridgeConnection::get(const QHostAddress &address, const QString &username, const QString &path, QObject *caller, const QString &methodName)
{
return m_address;
QString baseUrl = "http://" + address.toString() + "/api/" + username + "/";
QUrl url(baseUrl + path);
QNetworkRequest request;
request.setUrl(url);
QNetworkReply *reply = m_nam->get(request);
connect(reply, &QNetworkReply::finished, this, &HueBridgeConnection::slotGetFinished);
Caller c;
c.obj = caller;
c.method = methodName;
c.id = m_requestCounter;
m_requestMap.insert(reply, c);
return m_requestCounter++;
}
int HueBridgeConnection::put(const QHostAddress &address, const QString &username, const QString &path, const QVariantMap &data, QObject *caller, const QString &methodName)
{
QString baseUrl = "http://" + address.toString() + "/api/" + username + "/";
QUrl url(baseUrl + path);
QNetworkRequest request;
request.setUrl(url);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(data);
QByteArray jsonData = jsonDoc.toJson();
qDebug() << "putting" << url << jsonData;
QNetworkReply *reply = m_nam->put(request, jsonData);
connect(reply, &QNetworkReply::finished, this, &HueBridgeConnection::slotGetFinished);
Caller c;
c.obj = caller;
c.method = methodName;
c.id = m_requestCounter;
m_requestMap.insert(reply, c);
return m_requestCounter++;
}
void HueBridgeConnection::slotCreateUserFinished()
{
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
QByteArray data = reply->readAll();
int id = m_createUserMap.take(reply);
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
QVariantMap params;
QVariantMap errorMap;
errorMap.insert("description", "Failed to parse the bridge's response:" + error.errorString());
params.insert("error", errorMap);
emit createUserFinished(id, params);
return;
}
QVariantMap response = jsonDoc.toVariant().toList().first().toMap();
emit createUserFinished(id, response);
}
void HueBridgeConnection::slotGetFinished()
{
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
QByteArray data = reply->readAll();
Caller c = m_requestMap.take(reply);
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
QVariantMap params;
QVariantMap errorMap;
errorMap.insert("description", "Failed to parse the bridge's response:" + error.errorString());
params.insert("error", errorMap);
emit createUserFinished(c.id, params);
return;
}
QVariant response = jsonDoc.toVariant();
emit getFinished(c.id, response.toMap());
if (c.obj) {
qDebug() << "onvoking result slot at" << c.obj.data() << c.method.toLatin1().data();
metaObject()->invokeMethod(c.obj.data(), c.method.toLatin1().data(), Q_ARG(int, c.id), Q_ARG(QVariant, response));
}
}

View File

@ -21,19 +21,41 @@
#include <QObject>
#include <QHostAddress>
#include <QNetworkAccessManager>
#include <QPointer>
class Caller
{
public:
QPointer<QObject> obj;
QString method;
int id;
};
class HueBridgeConnection : public QObject
{
Q_OBJECT
public:
explicit HueBridgeConnection(const QHostAddress &address, const QString &username = QString(), QObject *parent = 0);
explicit HueBridgeConnection(QObject *parent = 0);
QHostAddress address() const;
QString username() const;
int createUser(const QHostAddress &address, const QString &username);
int get(const QHostAddress &address, const QString &username, const QString &path, QObject *caller, const QString &methodName);
int put(const QHostAddress &address, const QString &username, const QString &path, const QVariantMap &data, QObject *caller, const QString &methodName);
private slots:
void slotCreateUserFinished();
void slotGetFinished();
signals:
void createUserFinished(int id, const QVariantMap &map);
void getFinished(int id, const QVariantMap &map);
private:
QHostAddress m_address;
QString m_username;
QNetworkAccessManager *m_nam;
int m_requestCounter;
QHash<QNetworkReply*, int> m_createUserMap;
QHash<QNetworkReply*, Caller> m_requestMap;
};
#endif // HUEBRIDGECONNECTION_H

View File

@ -0,0 +1,422 @@
/*
* Copyright 2013 Michael Zanetti
*
* 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:
* Michael Zanetti <michael_zanetti@gmx.net>
*/
#include "light.h"
#include "huebridgeconnection.h"
#include <QColor>
#include <QDebug>
#include <QGenericMatrix>
Light::Light(const QHostAddress &ip, const QString &username, int id, QObject *parent):
QObject(parent),
m_bridge(new HueBridgeConnection(this)),
m_ip(ip),
m_username(username),
m_id(id),
m_on(false),
m_busyStateChangeId(-1),
m_hueDirty(false),
m_satDirty(false),
m_briDirty(false),
m_ctDirty(false),
m_xyDirty(false)
{
}
int Light::id() const
{
return m_id;
}
QString Light::name() const
{
return m_name;
}
void Light::setName(const QString &name)
{
if (m_name != name) {
QVariantMap params;
params.insert("name", name);
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id), params, this, "setDescriptionFinished");
}
}
QString Light::modelId() const
{
return m_modelId;
}
void Light::setModelId(const QString &modelId)
{
if (m_modelId != modelId) {
m_modelId = modelId;
}
}
QString Light::type() const
{
return m_type;
}
void Light::setType(const QString &type)
{
if (m_type != type) {
m_type = type;
}
}
QString Light::swversion() const
{
return m_swversion;
}
void Light::setSwversion(const QString &swversion)
{
if (m_swversion != swversion) {
m_swversion = swversion;
}
}
bool Light::on() const
{
return m_on;
}
void Light::setOn(bool on)
{
if (m_on != on) {
QVariantMap params;
params.insert("on", on);
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
}
}
quint8 Light::bri() const
{
return m_bri;
}
void Light::setBri(quint8 bri)
{
if (m_bri != bri) {
qDebug() << "setting brightness to" << bri << m_busyStateChangeId;
if (m_busyStateChangeId == -1) {
QVariantMap params;
params.insert("bri", bri);
params.insert("on", true);
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
} else {
m_dirtyBri = bri;
m_briDirty = true;
}
}
}
quint16 Light::hue() const
{
return m_hue;
}
void Light::setHue(quint16 hue)
{
if (m_hue != hue) {
m_hue = hue;
emit stateChanged();
}
}
quint8 Light::sat() const
{
return m_sat;
}
void Light::setSat(quint8 sat)
{
if (m_sat != sat) {
m_sat = sat;
emit stateChanged();
}
}
QColor Light::color() const
{
return QColor::fromHsv(m_hue * 360 / 65535, m_sat, 255);
}
void Light::setColor(const QColor &color)
{
// Transform from RGB to Hue/Sat
quint16 hue = color.hue() * 65535 / 360;
quint8 sat = color.saturation();
// Transform from RGB to XYZ
QGenericMatrix<3, 3, qreal> rgb2xyzMatrix;
rgb2xyzMatrix(0, 0) = 0.412453; rgb2xyzMatrix(0, 1) = 0.357580; rgb2xyzMatrix(0, 2) = 0.180423;
rgb2xyzMatrix(1, 0) = 0.212671; rgb2xyzMatrix(1, 1) = 0.715160; rgb2xyzMatrix(1, 2) = 0.072169;
rgb2xyzMatrix(2, 0) = 0.019334; rgb2xyzMatrix(2, 1) = 0.119193; rgb2xyzMatrix(2, 2) = 0.950227;
QGenericMatrix<1, 3, qreal> rgbMatrix;
rgbMatrix(0, 0) = 1.0 * color.red() / 255;
rgbMatrix(1, 0) = 1.0 * color.green() / 255;
rgbMatrix(2, 0) = 1.0 * color.blue() / 255;
QGenericMatrix<1, 3, qreal> xyzMatrix = rgb2xyzMatrix * rgbMatrix;
// transform from XYZ to CIELUV u' and v'
qreal u = 4*xyzMatrix(0, 0) / (xyzMatrix(0, 0) + 15*xyzMatrix(1, 0) + 3*xyzMatrix(2, 0));
qreal v = 9*xyzMatrix(1, 0) / (xyzMatrix(0, 0) + 15*xyzMatrix(1, 0) + 3*xyzMatrix(2, 0));
// Transform from CIELUV to (x,y)
qreal x = 27*u / (18*u - 48*v + 36);
qreal y = 12*v / (18*u - 48*v + 36);
qDebug() << "setting color" << color;
if (m_busyStateChangeId == -1) {
QVariantMap params;
params.insert("hue", hue);
params.insert("sat", sat);
// FIXME: There is a bug in the API that it doesn't report back the set state of "sat"
// Lets just assume it always succeeds
m_sat = sat;
// QVariantList xyList;
// xyList << x << y;
// params.insert("xy", xyList);
params.insert("on", true);
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
} else {
m_dirtyHue = hue;
m_hueDirty = true;
m_dirtySat = sat;
m_satDirty = true;
// m_xyDirty = true;
// m_dirtyXy = QPointF(x, y);
}
}
QPointF Light::xy() const
{
return m_xy;
}
void Light::setXy(const QPointF &xy)
{
if (m_xy != xy) {
m_xy = xy;
emit stateChanged();
}
}
quint16 Light::ct() const
{
return m_ct;
}
void Light::setCt(quint16 ct)
{
if (m_busyStateChangeId == -1) {
QVariantMap params;
params.insert("ct", ct);
params.insert("on", true);
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
} else {
m_dirtyCt = ct;
m_ctDirty = true;
}
}
QString Light::alert() const
{
return m_alert;
}
void Light::setAlert(const QString &alert)
{
if (m_alert != alert) {
m_alert = alert;
emit stateChanged();
}
}
QString Light::effect() const
{
return m_effect;
}
void Light::setEffect(const QString &effect)
{
if (m_effect != effect) {
QVariantMap params;
params.insert("effect", effect);
if (effect != "none") {
params.insert("on", true);
}
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
}
}
Light::ColorMode Light::colorMode() const
{
return m_colormode;
}
bool Light::reachable() const
{
return m_reachable;
}
void Light::refresh()
{
m_bridge->get(m_ip, m_username, "lights/" + QString::number(m_id), this, "responseReceived");
}
void Light::setReachable(bool reachable)
{
if (m_reachable != reachable) {
m_reachable = reachable;
emit stateChanged();
}
}
void Light::responseReceived(int id, const QVariant &response)
{
Q_UNUSED(id)
QVariantMap attributes = response.toMap();
m_name = attributes.value("name").toString();
setModelId(attributes.value("modelid").toString());
setType(attributes.value("type").toString());
setSwversion(attributes.value("swversion").toString());
QVariantMap stateMap = attributes.value("state").toMap();
m_on = stateMap.value("on").toBool();
m_bri = stateMap.value("bri").toInt();
m_hue = stateMap.value("hue").toInt();
m_sat = stateMap.value("sat").toInt();
m_xy = stateMap.value("xy").toPointF();
m_ct = stateMap.value("ct").toInt();
m_alert = stateMap.value("alert").toString();
m_effect = stateMap.value("effect").toString();
QString colorModeString = stateMap.value("colormode").toString();
if (colorModeString == "hs") {
m_colormode = ColorModeHS;
} else if (colorModeString == "xy") {
m_colormode = ColorModeXY;
} else if (colorModeString == "ct") {
m_colormode = ColorModeCT;
}
m_reachable = stateMap.value("reachable").toBool();
emit stateChanged();
// qDebug() << "got light response" << m_modelId << m_type << m_swversion << m_on << m_bri << m_reachable;
}
void Light::setDescriptionFinished(int id, const QVariant &response)
{
Q_UNUSED(id)
QVariantMap result = response.toList().first().toMap();
if (result.contains("success")) {
QVariantMap successMap = result.value("success").toMap();
if (successMap.contains("/lights/" + QString::number(m_id) + "/name")) {
m_name = successMap.value("/lights/" + QString::number(m_id) + "/name").toString();
emit stateChanged();
}
}
}
void Light::setStateFinished(int id, const QVariant &response)
{
foreach (const QVariant &resultVariant, response.toList()) {
QVariantMap result = resultVariant.toMap();
if (result.contains("success")) {
QVariantMap successMap = result.value("success").toMap();
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/on")) {
m_on = successMap.value("/lights/" + QString::number(m_id) + "/state/on").toBool();
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/hue")) {
m_hue = successMap.value("/lights/" + QString::number(m_id) + "/state/hue").toInt();
m_colormode = ColorModeHS;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/bri")) {
m_bri = successMap.value("/lights/" + QString::number(m_id) + "/state/bri").toInt();
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/sat")) {
m_sat = successMap.value("/lights/" + QString::number(m_id) + "/state/sat").toInt();
m_colormode = ColorModeHS;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/xy")) {
m_xy = successMap.value("/lights/" + QString::number(m_id) + "/state/xy").toPoint();
m_colormode = ColorModeXY;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/ct")) {
m_ct = successMap.value("/lights/" + QString::number(m_id) + "/state/ct").toInt();
m_colormode = ColorModeCT;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/effect")) {
m_effect = successMap.value("/lights/" + QString::number(m_id) + "/state/effect").toString();
}
}
}
emit stateChanged();
if (m_busyStateChangeId == id) {
m_busyStateChangeId = -1;
if (m_hueDirty || m_satDirty || m_briDirty) {
QVariantMap params;
if (m_hueDirty) {
params.insert("hue", m_dirtyHue);
m_hueDirty = false;
}
if (m_satDirty) {
params.insert("sat", m_dirtySat);
m_satDirty = false;
}
if (m_briDirty) {
params.insert("bri", m_dirtyBri);
m_briDirty = false;
}
// FIXME: There is a bug in the API that it doesn't report back the set state of "sat"
// Lets just assume it always succeeds
m_sat = m_dirtySat;
m_busyStateChangeId = m_bridge->put(QHostAddress(m_ip), m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
} else if(m_ctDirty) {
QVariantMap params;
params.insert("ct", m_dirtyCt);
m_ctDirty = false;
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
} else if (m_xyDirty) {
QVariantMap params;
QVariantList xyList;
xyList << m_dirtyXy.x() << m_dirtyXy.y();
params.insert("xy", xyList);
m_xyDirty = false;
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
}
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright 2013 Michael Zanetti
*
* 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:
* Michael Zanetti <michael_zanetti@gmx.net>
*/
#ifndef LIGHT_H
#define LIGHT_H
#include <QObject>
#include <QPointF>
#include <QColor>
#include <QHostAddress>
class HueBridgeConnection;
class Light: public QObject
{
Q_OBJECT
public:
enum ColorMode {
ColorModeHS,
ColorModeXY,
ColorModeCT
};
Light(const QHostAddress &ip, const QString &username, int id, QObject *parent = 0);
int id() const;
QString name() const;
void setName(const QString &name);
QString modelId() const;
void setModelId(const QString &modelId);
QString type() const;
void setType(const QString &type);
QString swversion() const;
void setSwversion(const QString &swversion);
// LightInterface implementation
bool on() const;
quint8 bri() const;
quint16 hue() const;
quint8 sat() const;
QColor color() const;
QPointF xy() const;
quint16 ct() const;
QString alert() const;
QString effect() const;
ColorMode colorMode() const;
bool reachable() const;
public slots:
void refresh();
void setOn(bool on);
void setBri(quint8 bri);
void setHue(quint16 hue);
void setSat(quint8 sat);
void setColor(const QColor &color);
void setXy(const QPointF &xy);
void setCt(quint16 ct);
void setAlert(const QString &alert);
void setEffect(const QString &effect);
signals:
void stateChanged();
private slots:
void responseReceived(int id, const QVariant &response);
void setDescriptionFinished(int id, const QVariant &response);
void setStateFinished(int id, const QVariant &response);
private:
void setReachable(bool reachable);
HueBridgeConnection *m_bridge;
QHostAddress m_ip;
QString m_username;
int m_id;
QString m_name;
QString m_modelId;
QString m_type;
QString m_swversion;
bool m_on;
quint8 m_bri;
quint16 m_hue;
quint8 m_sat;
QPointF m_xy;
quint16 m_ct;
QString m_alert;
QString m_effect;
ColorMode m_colormode;
bool m_reachable;
int m_busyStateChangeId;
bool m_hueDirty;
quint16 m_dirtyHue;
bool m_satDirty;
quint8 m_dirtySat;
bool m_briDirty;
quint8 m_dirtyBri;
bool m_ctDirty;
quint16 m_dirtyCt;
bool m_xyDirty;
QPointF m_dirtyXy;
};
#endif

View File

@ -7,12 +7,15 @@ QT += network
SOURCES += \
devicepluginphilipshue.cpp \
discovery.cpp \
huebridgeconnection.cpp
huebridgeconnection.cpp \
light.cpp
HEADERS += \
devicepluginphilipshue.h \
discovery.h \
huebridgeconnection.h
huebridgeconnection.h \
light.h \
lightinterface.h

View File

@ -114,5 +114,17 @@ QVariantMap ActionHandler::statusToReply(DeviceManager::DeviceError status, cons
QVariantMap returns;
returns.insert("success", status == DeviceManager::DeviceErrorNoError);
returns.insert("errorMessage", errorMessage);
switch (status) {
case DeviceManager::DeviceErrorNoError:
break;
case DeviceManager::DeviceErrorDeviceNotFound:
returns.insert("errorMessage", QString("Device not found: %1").arg(errorMessage));
break;
case DeviceManager::DeviceErrorSetupFailed:
returns.insert("errorMessage", QString("Device setup failed: %1").arg(errorMessage));
break;
}
return returns;
}

View File

@ -620,15 +620,6 @@ void DeviceHandler::pairingFinished(const QUuid &pairingTransactionId, DeviceMan
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();
m_asynDeviceAdditions.insert(deviceId, reply);
}