more work on the Hue plugin
This commit is contained in:
parent
5a0d5fb7c2
commit
fcdf3e1e0b
@ -456,6 +456,7 @@ DeviceClass DeviceManager::findDeviceClass(const DeviceClassId &deviceClassId) c
|
|||||||
its \l{DevicePlugin}. Then will dispatch the execution to the \l{DevicePlugin}.*/
|
its \l{DevicePlugin}. Then will dispatch the execution to the \l{DevicePlugin}.*/
|
||||||
QPair<DeviceManager::DeviceError, QString> DeviceManager::executeAction(const Action &action)
|
QPair<DeviceManager::DeviceError, QString> DeviceManager::executeAction(const Action &action)
|
||||||
{
|
{
|
||||||
|
qDebug() << "should execute action";
|
||||||
foreach (Device *device, m_configuredDevices) {
|
foreach (Device *device, m_configuredDevices) {
|
||||||
if (action.deviceId() == device->id()) {
|
if (action.deviceId() == device->id()) {
|
||||||
// found device
|
// found device
|
||||||
@ -464,6 +465,7 @@ QPair<DeviceManager::DeviceError, QString> DeviceManager::executeAction(const Ac
|
|||||||
DeviceClass deviceClass = findDeviceClass(device->deviceClassId());
|
DeviceClass deviceClass = findDeviceClass(device->deviceClassId());
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
|
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
|
||||||
|
qDebug() << "checking" << actionType.id() << action.actionTypeId();
|
||||||
if (actionType.id() == action.actionTypeId()) {
|
if (actionType.id() == action.actionTypeId()) {
|
||||||
QPair<DeviceError, QString> paramCheck = verifyParams(actionType.parameters(), action.params());
|
QPair<DeviceError, QString> paramCheck = verifyParams(actionType.parameters(), action.params());
|
||||||
if (paramCheck.first != DeviceErrorNoError) {
|
if (paramCheck.first != DeviceErrorNoError) {
|
||||||
@ -736,7 +738,12 @@ void DeviceManager::slotPairingFinished(const QUuid &pairingTransactionId, Devic
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ok... pairing went fine... Let consumers know about it and inform them about the ongoing setup with a deviceId.
|
||||||
DeviceId id = DeviceId::createDeviceId();
|
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 *device = new Device(plugin->pluginId(), id, deviceClassId, this);
|
||||||
device->setName(deviceClass.name());
|
device->setName(deviceClass.name());
|
||||||
device->setParams(params);
|
device->setParams(params);
|
||||||
@ -745,19 +752,22 @@ void DeviceManager::slotPairingFinished(const QUuid &pairingTransactionId, Devic
|
|||||||
switch (setupStatus.first) {
|
switch (setupStatus.first) {
|
||||||
case DeviceSetupStatusFailure:
|
case DeviceSetupStatusFailure:
|
||||||
qWarning() << "Device setup failed. Not adding device to system.";
|
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;
|
delete device;
|
||||||
emit pairingFinished(pairingTransactionId, DeviceErrorSetupFailed, QString("Device setup failed: %1").arg(setupStatus.second));
|
break;
|
||||||
return;
|
|
||||||
case DeviceSetupStatusAsync:
|
case DeviceSetupStatusAsync:
|
||||||
|
return;
|
||||||
case DeviceSetupStatusSuccess:
|
case DeviceSetupStatusSuccess:
|
||||||
qDebug() << "Device setup complete.";
|
qDebug() << "Device setup complete.";
|
||||||
|
newDevices.append(id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_configuredDevices.append(device);
|
m_configuredDevices.append(device);
|
||||||
storeConfiguredDevices();
|
storeConfiguredDevices();
|
||||||
|
|
||||||
emit pairingFinished(pairingTransactionId, DeviceErrorNoError, QString(), id);
|
emit deviceSetupFinished(device, DeviceError::DeviceErrorNoError, QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value)
|
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> ¶ms, bool requireAll)
|
QPair<DeviceManager::DeviceError, QString> DeviceManager::verifyParams(const QList<ParamType> paramTypes, const QList<Param> ¶ms, bool requireAll)
|
||||||
{
|
{
|
||||||
foreach (const Param ¶m, params) {
|
foreach (const Param ¶m, params) {
|
||||||
|
qDebug() << "verifying param" << param.name() << paramTypes;
|
||||||
QPair<DeviceManager::DeviceError, QString> result = verifyParam(paramTypes, param);
|
QPair<DeviceManager::DeviceError, QString> result = verifyParam(paramTypes, param);
|
||||||
if (result.first != DeviceErrorNoError) {
|
if (result.first != DeviceErrorNoError) {
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -81,6 +81,7 @@ signals:
|
|||||||
void pairingFinished(const QUuid &pairingTransactionId, 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 actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status, const QString &errorMessage);
|
||||||
void configValueChanged(const QString ¶mName, const QVariant &value);
|
void configValueChanged(const QString ¶mName, const QVariant &value);
|
||||||
|
void autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DeviceManager *deviceManager() const;
|
DeviceManager *deviceManager() const;
|
||||||
|
|||||||
@ -25,10 +25,6 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
|
||||||
VendorId hueVendorId = VendorId("0ae1e001-2aa6-47ed-b8c0-334c3728a68f");
|
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");
|
DeviceClassId hueDeviceClassId = DeviceClassId("d8f4c397-e05e-47c1-8917-8e72d4d0d47c");
|
||||||
|
|
||||||
StateTypeId hueColorStateTypeId = StateTypeId("d25423e7-b924-4b20-80b6-77eecc65d089");
|
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():
|
DevicePluginPhilipsHue::DevicePluginPhilipsHue():
|
||||||
m_discovery(new Discovery(this))
|
m_discovery(new Discovery(this))
|
||||||
{
|
{
|
||||||
connect(m_discovery, &Discovery::discoveryDone, this, &DevicePluginPhilipsHue::discoveryDone);
|
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
|
QList<Vendor> DevicePluginPhilipsHue::supportedVendors() const
|
||||||
@ -71,6 +72,8 @@ QList<DeviceClass> DevicePluginPhilipsHue::supportedDevices() const
|
|||||||
paramTypes.append(ipParam);
|
paramTypes.append(ipParam);
|
||||||
ParamType usernameParam("username", QVariant::String);
|
ParamType usernameParam("username", QVariant::String);
|
||||||
paramTypes.append(usernameParam);
|
paramTypes.append(usernameParam);
|
||||||
|
ParamType numberParam("number", QVariant::Int, -1);
|
||||||
|
paramTypes.append(numberParam);
|
||||||
deviceClassHue.setParamTypes(paramTypes);
|
deviceClassHue.setParamTypes(paramTypes);
|
||||||
|
|
||||||
QList<StateType> hueStates;
|
QList<StateType> hueStates;
|
||||||
@ -81,20 +84,32 @@ QList<DeviceClass> DevicePluginPhilipsHue::supportedDevices() const
|
|||||||
colorState.setDefaultValue(QColor(Qt::black));
|
colorState.setDefaultValue(QColor(Qt::black));
|
||||||
hueStates.append(colorState);
|
hueStates.append(colorState);
|
||||||
|
|
||||||
|
StateType powerState(huePowerStateTypeId);
|
||||||
|
powerState.setName("power");
|
||||||
|
powerState.setType(QVariant::Bool);
|
||||||
|
powerState.setDefaultValue(false);
|
||||||
|
hueStates.append(powerState);
|
||||||
|
|
||||||
deviceClassHue.setStateTypes(hueStates);
|
deviceClassHue.setStateTypes(hueStates);
|
||||||
|
|
||||||
QList<ActionType> hueActons;
|
QList<ActionType> hueActons;
|
||||||
|
|
||||||
ActionType setColorAction(setHueColorActionTypeId);
|
ActionType setColorAction(hueSetColorActionTypeId);
|
||||||
setColorAction.setName("Set color");
|
setColorAction.setName("Set color");
|
||||||
|
|
||||||
QList<ParamType> actionParamsSetColor;
|
QList<ParamType> actionParamsSetColor;
|
||||||
ParamType actionParamSetColor("color", QVariant::Color);
|
ParamType actionParamSetColor("color", QVariant::Color);
|
||||||
actionParamsSetColor.append(actionParamSetColor);
|
actionParamsSetColor.append(actionParamSetColor);
|
||||||
setColorAction.setParameters(actionParamsSetColor);
|
setColorAction.setParameters(actionParamsSetColor);
|
||||||
|
|
||||||
hueActons.append(setColorAction);
|
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);
|
deviceClassHue.setActions(hueActons);
|
||||||
|
|
||||||
ret.append(deviceClassHue);
|
ret.append(deviceClassHue);
|
||||||
@ -151,7 +166,30 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceC
|
|||||||
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::setupDevice(Device *device)
|
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||||
{
|
{
|
||||||
qDebug() << "setupDevice" << device->params();
|
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> ¶ms)
|
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::confirmPairing(const QUuid &pairingTransactionId, const DeviceClassId &deviceClassId, const QList<Param> ¶ms)
|
||||||
@ -175,36 +213,30 @@ QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::confirm
|
|||||||
return reportDeviceSetup(DeviceManager::DeviceSetupStatusFailure, "Missing parameter: username");
|
return reportDeviceSetup(DeviceManager::DeviceSetupStatusFailure, "Missing parameter: username");
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap createUserParams;
|
int id = m_bridge->createUser(QHostAddress(ipParam.value().toString()), usernameParam.value().toString());
|
||||||
createUserParams.insert("devicetype", "guh");
|
PairingInfo pi;
|
||||||
createUserParams.insert("username", usernameParam.value().toString());
|
pi.pairingTransactionId = pairingTransactionId;
|
||||||
|
pi.ipParam = ipParam;
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(createUserParams);
|
pi.usernameParam = usernameParam;
|
||||||
QByteArray data = jsonDoc.toJson();
|
m_pairings.insert(id, pi);
|
||||||
|
|
||||||
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);
|
|
||||||
return reportDeviceSetup(DeviceManager::DeviceSetupStatusAsync);
|
return reportDeviceSetup(DeviceManager::DeviceSetupStatusAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<DeviceManager::DeviceError, QString> DevicePluginPhilipsHue::executeAction(Device *device, const Action &action)
|
QPair<DeviceManager::DeviceError, QString> DevicePluginPhilipsHue::executeAction(Device *device, const Action &action)
|
||||||
{
|
{
|
||||||
// if (!m_bobClient->connected()) {
|
qDebug() << "Should execute action in hue plugin";
|
||||||
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);
|
Light *light = m_lights.key(device);
|
||||||
// return report();
|
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)
|
void DevicePluginPhilipsHue::discoveryDone(const QList<QHostAddress> &bridges)
|
||||||
@ -218,6 +250,8 @@ void DevicePluginPhilipsHue::discoveryDone(const QList<QHostAddress> &bridges)
|
|||||||
params.append(param);
|
params.append(param);
|
||||||
Param userParam("username", "guh-" + QUuid::createUuid().toString().remove(QRegExp("[\\{\\}]*")).remove(QRegExp("\\-[0-9a-f\\-]*")));
|
Param userParam("username", "guh-" + QUuid::createUuid().toString().remove(QRegExp("[\\{\\}]*")).remove(QRegExp("\\-[0-9a-f\\-]*")));
|
||||||
params.append(userParam);
|
params.append(userParam);
|
||||||
|
Param numberParam("number", -1);
|
||||||
|
params.append(numberParam);
|
||||||
descriptor.setParams(params);
|
descriptor.setParams(params);
|
||||||
deviceDescriptors.append(descriptor);
|
deviceDescriptors.append(descriptor);
|
||||||
}
|
}
|
||||||
@ -225,28 +259,69 @@ void DevicePluginPhilipsHue::discoveryDone(const QList<QHostAddress> &bridges)
|
|||||||
emit devicesDiscovered(hueDeviceClassId, deviceDescriptors);
|
emit devicesDiscovered(hueDeviceClassId, deviceDescriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevicePluginPhilipsHue::createUserFinished()
|
void DevicePluginPhilipsHue::createUserFinished(int id, const QVariantMap &response)
|
||||||
{
|
{
|
||||||
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
|
qDebug() << "createuser response" << response;
|
||||||
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();
|
|
||||||
|
|
||||||
|
PairingInfo pairingInfo = m_pairings.take(id);
|
||||||
if (response.contains("error")) {
|
if (response.contains("error")) {
|
||||||
qDebug() << "Failed to pair Hue bridge:" << response.value("error").toMap().value("description");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit pairingFinished(pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess, QString());
|
// Paired successfully, check how many lightbulbs there are
|
||||||
qDebug() << "response" << response << data;
|
|
||||||
|
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 ¶ms)
|
||||||
|
{
|
||||||
|
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 ¶ms)
|
||||||
|
{
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,8 @@
|
|||||||
#include "plugin/deviceplugin.h"
|
#include "plugin/deviceplugin.h"
|
||||||
#include "discovery.h"
|
#include "discovery.h"
|
||||||
#include "huebridgeconnection.h"
|
#include "huebridgeconnection.h"
|
||||||
|
#include "light.h"
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
|
|
||||||
class DevicePluginPhilipsHue: public DevicePlugin
|
class DevicePluginPhilipsHue: public DevicePlugin
|
||||||
@ -58,14 +58,30 @@ public slots:
|
|||||||
private slots:
|
private slots:
|
||||||
void discoveryDone(const QList<QHostAddress> &bridges);
|
void discoveryDone(const QList<QHostAddress> &bridges);
|
||||||
|
|
||||||
void createUserFinished();
|
void createUserFinished(int id, const QVariantMap ¶ms);
|
||||||
|
void getLightsFinished(int id, const QVariantMap ¶ms);
|
||||||
|
void getFinished(int id, const QVariantMap ¶ms);
|
||||||
|
|
||||||
|
void lightStateChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<Param> m_config;
|
QList<Param> m_config;
|
||||||
Discovery *m_discovery;
|
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
|
#endif // DEVICEPLUGINBOBLIGHT_H
|
||||||
|
|||||||
@ -18,22 +18,118 @@
|
|||||||
|
|
||||||
#include "huebridgeconnection.h"
|
#include "huebridgeconnection.h"
|
||||||
|
|
||||||
#include <QUuid>
|
#include <QJsonDocument>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
HueBridgeConnection::HueBridgeConnection(const QHostAddress &address, const QString &username, QObject *parent) :
|
HueBridgeConnection::HueBridgeConnection(QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent)
|
||||||
m_address(address),
|
|
||||||
m_username(username)
|
|
||||||
{
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -21,19 +21,41 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
class Caller
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QPointer<QObject> obj;
|
||||||
|
QString method;
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
class HueBridgeConnection : public QObject
|
class HueBridgeConnection : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit HueBridgeConnection(const QHostAddress &address, const QString &username = QString(), QObject *parent = 0);
|
explicit HueBridgeConnection(QObject *parent = 0);
|
||||||
|
|
||||||
QHostAddress address() const;
|
int createUser(const QHostAddress &address, const QString &username);
|
||||||
QString username() const;
|
|
||||||
|
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:
|
private:
|
||||||
QHostAddress m_address;
|
QNetworkAccessManager *m_nam;
|
||||||
QString m_username;
|
int m_requestCounter;
|
||||||
|
QHash<QNetworkReply*, int> m_createUserMap;
|
||||||
|
QHash<QNetworkReply*, Caller> m_requestMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HUEBRIDGECONNECTION_H
|
#endif // HUEBRIDGECONNECTION_H
|
||||||
|
|||||||
422
plugins/deviceplugins/philipshue/light.cpp
Normal file
422
plugins/deviceplugins/philipshue/light.cpp
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
129
plugins/deviceplugins/philipshue/light.h
Normal file
129
plugins/deviceplugins/philipshue/light.h
Normal 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
|
||||||
@ -7,12 +7,15 @@ QT += network
|
|||||||
SOURCES += \
|
SOURCES += \
|
||||||
devicepluginphilipshue.cpp \
|
devicepluginphilipshue.cpp \
|
||||||
discovery.cpp \
|
discovery.cpp \
|
||||||
huebridgeconnection.cpp
|
huebridgeconnection.cpp \
|
||||||
|
light.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
devicepluginphilipshue.h \
|
devicepluginphilipshue.h \
|
||||||
discovery.h \
|
discovery.h \
|
||||||
huebridgeconnection.h
|
huebridgeconnection.h \
|
||||||
|
light.h \
|
||||||
|
lightinterface.h
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -114,5 +114,17 @@ QVariantMap ActionHandler::statusToReply(DeviceManager::DeviceError status, cons
|
|||||||
QVariantMap returns;
|
QVariantMap returns;
|
||||||
returns.insert("success", status == DeviceManager::DeviceErrorNoError);
|
returns.insert("success", status == DeviceManager::DeviceErrorNoError);
|
||||||
returns.insert("errorMessage", errorMessage);
|
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;
|
return returns;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -620,15 +620,6 @@ void DeviceHandler::pairingFinished(const QUuid &pairingTransactionId, DeviceMan
|
|||||||
qDebug() << "not for me";
|
qDebug() << "not for me";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QVariantMap returns;
|
|
||||||
if (status == DeviceManager::DeviceErrorNoError) {
|
m_asynDeviceAdditions.insert(deviceId, reply);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user