diff --git a/guh.pri b/guh.pri index 5e5d8401..6e39a50e 100644 --- a/guh.pri +++ b/guh.pri @@ -2,7 +2,7 @@ GUH_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') # define JSON protocol version -JSON_PROTOCOL_VERSION=25 +JSON_PROTOCOL_VERSION=26 DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" JSON_PROTOCOL_VERSION=\\\"$${JSON_PROTOCOL_VERSION}\\\" @@ -25,4 +25,3 @@ enable433gpio { top_srcdir=$$PWD top_builddir=$$shadowed($$PWD) - diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index 2690a0de..09b38f23 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -624,11 +624,22 @@ DeviceManager::DeviceError DeviceManager::removeConfiguredDevice(const DeviceId m_configuredDevices.removeAll(device); m_devicePlugins.value(device->pluginId())->deviceRemoved(device); - m_pluginTimerUsers.removeAll(device); - if (m_pluginTimerUsers.isEmpty()) { - m_pluginTimer.stop(); + // check if this plugin still needs the guhTimer call + bool pluginNeedsTimer = false; + foreach (Device* d, m_configuredDevices) { + if (d->pluginId() == device->pluginId()) { + pluginNeedsTimer = true; + break; + } } + // if this plugin doesn't need any longer the guhTimer call + if (!pluginNeedsTimer) { + m_pluginTimerUsers.removeAll(plugin(device->pluginId())); + if (m_pluginTimerUsers.isEmpty()) { + m_pluginTimer.stop(); + } + } device->deleteLater(); QSettings settings(m_settingsFile); @@ -929,7 +940,9 @@ void DeviceManager::slotDeviceSetupFinished(Device *device, DeviceManager::Devic // Additionally fire off one event to initialize stuff QTimer::singleShot(0, this, SLOT(timerEvent())); } - m_pluginTimerUsers.append(device); + if (!m_pluginTimerUsers.contains(plugin)) { + m_pluginTimerUsers.append(plugin); + } } // if this is a async device edit result @@ -1123,10 +1136,8 @@ void DeviceManager::upnpNotifyReceived(const QByteArray ¬ifyData) void DeviceManager::timerEvent() { - foreach (Device *device, m_configuredDevices) { - DeviceClass deviceClass = m_supportedDevices.value(device->deviceClassId()); - DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); - if (plugin && plugin->requiredHardware().testFlag(HardwareResourceTimer)) { + foreach (DevicePlugin *plugin, m_pluginTimerUsers) { + if (plugin->requiredHardware().testFlag(HardwareResourceTimer)) { plugin->guhTimer(); } } @@ -1175,7 +1186,9 @@ DeviceManager::DeviceSetupStatus DeviceManager::setupDevice(Device *device) // Additionally fire off one event to initialize stuff QTimer::singleShot(0, this, SLOT(timerEvent())); } - m_pluginTimerUsers.append(device); + if (!m_pluginTimerUsers.contains(plugin)) { + m_pluginTimerUsers.append(plugin); + } } connect(device, SIGNAL(stateValueChanged(QUuid,QVariant)), this, SLOT(slotDeviceStateValueChanged(QUuid,QVariant))); diff --git a/libguh/devicemanager.h b/libguh/devicemanager.h index 60a792ec..079a465a 100644 --- a/libguh/devicemanager.h +++ b/libguh/devicemanager.h @@ -158,7 +158,7 @@ private: QHash m_supportedVendors; QHash > m_vendorDeviceMap; QHash m_supportedDevices; - QList m_configuredDevices; + QList m_configuredDevices; QHash m_discoveredDevices; QHash m_devicePlugins; @@ -168,15 +168,15 @@ private: // Hardware Resources Radio433* m_radio433; QTimer m_pluginTimer; - QList m_pluginTimerUsers; + QList m_pluginTimerUsers; NetworkManager *m_networkManager; QHash > m_pairingsJustAdd; QHash > m_pairingsDiscovery; - QList m_asyncDeviceEdit; + QList m_asyncDeviceEdit; - QList m_discoveringPlugins; + QList m_discoveringPlugins; friend class DevicePlugin; }; diff --git a/libguh/plugin/deviceplugin.cpp b/libguh/plugin/deviceplugin.cpp index b59b869e..4900c00d 100644 --- a/libguh/plugin/deviceplugin.cpp +++ b/libguh/plugin/deviceplugin.cpp @@ -247,7 +247,13 @@ QList DevicePlugin::supportedDevices() const actionType.setName("set " + st.value("name").toString()); // Note: fields already checked in StateType ParamType paramType(st.value("name").toString(), t, st.value("defaultValue").toVariant()); - // states don't have allowed values + if (st.contains("allowedValues")) { + QVariantList allowedValues; + foreach (const QJsonValue &allowedTypesJson, st.value("allowedValues").toArray()) { + allowedValues.append(allowedTypesJson.toVariant()); + } + paramType.setAllowedValues(allowedValues); + } // states don't have input types paramType.setUnit(unitStringToUnit(st.value("unit").toString())); paramType.setLimits(st.value("minValue").toVariant(), st.value("maxValue").toVariant()); @@ -667,6 +673,8 @@ Types::Unit DevicePlugin::unitStringToUnit(const QString &unitString) const return Types::UnitDegreeCelsius; } else if (unitString == "DegreeKelvin") { return Types::UnitDegreeKelvin; + } else if (unitString == "Mired") { + return Types::UnitMired; } else if (unitString == "MilliBar") { return Types::UnitMilliBar; } else if (unitString == "Bar") { diff --git a/libguh/typeutils.h b/libguh/typeutils.h index 799bd69c..0a874b0f 100644 --- a/libguh/typeutils.h +++ b/libguh/typeutils.h @@ -89,6 +89,7 @@ public: UnitRadiant, UnitDegreeCelsius, UnitDegreeKelvin, + UnitMired, UnitMilliBar, UnitBar, UnitPascal, diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp b/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp index 32652cfa..25e51937 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp @@ -47,15 +47,15 @@ #include "devicepluginphilipshue.h" -#include "plugin/device.h" #include "devicemanager.h" +#include "plugin/device.h" #include "types/param.h" -#include "huebridgeconnection.h" #include "plugininfo.h" #include #include #include +#include VendorId hueVendorId = VendorId(""); @@ -74,26 +74,11 @@ StateTypeId hueReachableStateTypeId = StateTypeId("15794d26-fde8-4a61-8f83-d7830 DevicePluginPhilipsHue::DevicePluginPhilipsHue() { - m_bridge = new HueBridgeConnection(this); - connect(m_bridge, &HueBridgeConnection::createUserFinished, this, &DevicePluginPhilipsHue::createUserFinished); - connect(m_bridge, &HueBridgeConnection::getFinished, this, &DevicePluginPhilipsHue::getFinished); } DeviceManager::HardwareResources DevicePluginPhilipsHue::requiredHardware() const { - return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery; -} - -void DevicePluginPhilipsHue::startMonitoringAutoDevices() -{ - // TODO: We could call the bridge to discover new light bulbs here maybe? - // Although we maybe want to think of a user triggered approach to do such things. -} - -QList DevicePluginPhilipsHue::configurationDescription() const -{ - QList params; - return params; + return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery | DeviceManager::HardwareResourceNetworkManager; } DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) @@ -106,208 +91,462 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceC DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device) { - Light *light = nullptr; + // hue bridge + if (device->deviceClassId() == hueBridgeDeviceClassId) { + // unconfigured bridges (from pairing) + foreach (HueBridge *b, m_unconfiguredBridges) { + if (b->username() == device->paramValue("username").toString()) { + m_unconfiguredBridges.removeOne(b); - // 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()); - device->setParamValue("name", QString("Hue light %1").arg(light->id())); - } else { - // this shouldn't ever happen - qWarning() << "Device not configured yet and no discovered devices around. This should not happen."; - return DeviceManager::DeviceSetupStatusFailure; + // set data which was not known during discovery + device->setParamValue("name", b->name()); + device->setParamValue("zigbee channel", b->zigbeeChannel()); + device->setParamValue("api version", b->apiVersion()); + device->setParamValue("mac address", b->macAddress()); + device->setStateValue(bridgeReachableStateTypeId, true); + m_bridges.insert(b, device); + + // now add the child lights from this bridge as auto device + QList descriptors; + foreach (HueLight *light, b->lights()) { + DeviceDescriptor descriptor(hueLightDeviceClassId, "Philips Hue Light", light->name()); + ParamList params; + params.append(Param("name", light->name())); + params.append(Param("username", light->username())); + params.append(Param("bridge", device->id().toString())); + params.append(Param("host address", light->hostAddress().toString())); + params.append(Param("model id", light->modelId())); + params.append(Param("light id", light->lightId())); + descriptor.setParams(params); + descriptors.append(descriptor); + } + emit autoDevicesAppeared(hueLightDeviceClassId, descriptors); + + return DeviceManager::DeviceSetupStatusSuccess; + } } - } 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()); + + // loaded bridge + HueBridge *bridge = new HueBridge(device->paramValue("username").toString(), + QHostAddress(device->paramValue("host address").toString())); + + bridge->setApiVersion(device->paramValue("api version").toString()); + bridge->setMacAddress(device->paramValue("mac address").toString()); + bridge->setName(device->paramValue("name").toString()); + bridge->setZigbeeChannel(device->paramValue("zigbee channel").toInt()); + + m_bridges.insert(bridge, device); + + return DeviceManager::DeviceSetupStatusSuccess; } - connect(light, &Light::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); - light->refresh(); + // hue lights + if (device->deviceClassId() == hueLightDeviceClassId) { - m_lights.insert(light, device); - m_asyncSetups.insert(light, device); + HueLight *hueLight = 0; - // If we have more unconfigured lights around, lets add them as auto devices - QList descriptorList; - while (!m_unconfiguredLights.isEmpty()) { - Light *light = m_unconfiguredLights.takeFirst(); - DeviceDescriptor descriptor(hueDeviceClassId, light->name()); - ParamList params; - params.append(Param("name", light->name())); - params.append(Param("number", light->id())); - params.append(Param("ip", light->ip().toString())); - params.append(Param("username", light->username())); - descriptor.setParams(params); - descriptorList.append(descriptor); - } - if (!descriptorList.isEmpty()) { - metaObject()->invokeMethod(this, "autoDevicesAppeared", Qt::QueuedConnection, Q_ARG(DeviceClassId, hueDeviceClassId), Q_ARG(QList, descriptorList)); + // check if this is a unconfigured light + for (int i = 0; i < m_unconfiguredLights.count(); i++) { + if (m_unconfiguredLights.at(i)->username() == device->paramValue("username").toString()) { + hueLight = m_unconfiguredLights.takeAt(i); + break; + } + } + + // check if this is a light from settings + if (hueLight == 0) { + hueLight = new HueLight(device->paramValue("light id").toInt(), + QHostAddress(device->paramValue("host address").toString()), + device->paramValue("name").toString(), + device->paramValue("username").toString(), + device->paramValue("model id").toString(), + DeviceId(device->paramValue("bridge").toString()), + this); + + connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); + } + + device->setName(hueLight->name()); + m_lights.insert(hueLight, device); + refreshLight(device); } - return DeviceManager::DeviceSetupStatusAsync; + return DeviceManager::DeviceSetupStatusSuccess; } void DevicePluginPhilipsHue::deviceRemoved(Device *device) { - if (!m_lights.values().contains(device)) { - return; - } - - Light *light = m_lights.key(device); - m_lights.remove(light); - light->deleteLater(); + Q_UNUSED(device) } void DevicePluginPhilipsHue::upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) { - foreach (const UpnpDeviceDescriptor &descriptor, upnpDeviceDescriptorList) { - qDebug() << descriptor; - } - QList deviceDescriptors; foreach (const UpnpDeviceDescriptor &upnpDevice, upnpDeviceDescriptorList) { if (upnpDevice.modelDescription().contains("Philips")) { - DeviceDescriptor descriptor(hueDeviceClassId, "Philips hue bridge", upnpDevice.hostAddress().toString()); + DeviceDescriptor descriptor(hueBridgeDeviceClassId, "Philips Hue Bridge", upnpDevice.hostAddress().toString()); ParamList params; - params.append(Param("ip", upnpDevice.hostAddress().toString())); + params.append(Param("name", QString())); + params.append(Param("host address", upnpDevice.hostAddress().toString())); params.append(Param("username", "guh-" + QUuid::createUuid().toString().remove(QRegExp("[\\{\\}]*")).remove(QRegExp("\\-[0-9a-f\\-]*")))); - params.append(Param("number", -1)); + params.append(Param("mac address", QString())); + params.append(Param("api version", QString())); + params.append(Param("zigbee channel", -1)); descriptor.setParams(params); deviceDescriptors.append(descriptor); } } - emit devicesDiscovered(hueDeviceClassId, deviceDescriptors); + emit devicesDiscovered(hueBridgeDeviceClassId, deviceDescriptors); } DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms) { - Q_UNUSED(deviceClassId) - Param ipParam; - foreach (const Param ¶m, params) { - if (param.name() == "ip") { - ipParam = param; - } - } - if (!ipParam.isValid()) { - qWarning() << "Missing parameter: ip"; - return DeviceManager::DeviceSetupStatusFailure; - } - Param usernameParam; - foreach (const Param ¶m, params) { - if (param.name() == "username") { - usernameParam = param; - } - } - if (!usernameParam.isValid()) { - qWarning() << "Missing parameter: username"; + if (deviceClassId != hueBridgeDeviceClassId) { return DeviceManager::DeviceSetupStatusFailure; } - 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); + PairingInfo pairingInfo; + pairingInfo.pairingTransactionId = pairingTransactionId; + pairingInfo.host = QHostAddress(params.paramValue("host address").toString()); + pairingInfo.username = params.paramValue("username").toString(); + + QVariantMap createUserParams; + createUserParams.insert("devicetype", "guh"); + createUserParams.insert("username", pairingInfo.username); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(createUserParams); + + QNetworkRequest request(QUrl("http://" + pairingInfo.host.toString() + "/api")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + QNetworkReply *reply = networkManagerPost(request, jsonDoc.toJson()); + + m_pairingRequests.insert(reply, pairingInfo); + return DeviceManager::DeviceSetupStatusAsync; } +void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) +{ + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + // create user finished + if (m_pairingRequests.keys().contains(reply)) { + PairingInfo pairingInfo = m_pairingRequests.take(reply); + + // check HTTP status code + if (status != 200) { + qWarning() << "Request error:" << status << reply->errorString(); + reply->deleteLater(); + return; + } + + QByteArray data = reply->readAll(); + processPairingResponse(pairingInfo, data); + + } else if (m_informationRequests.keys().contains(reply)) { + PairingInfo pairingInfo = m_informationRequests.take(reply); + + // check HTTP status code + if (status != 200) { + qWarning() << "Request error:" << status << reply->errorString(); + reply->deleteLater(); + return; + } + + QByteArray data = reply->readAll(); + processInformationResponse(pairingInfo, data); + } else if (m_lightRefreshRequests.keys().contains(reply)) { + + Device *device = m_lightRefreshRequests.take(reply); + + // check HTTP status code + if (status != 200) { + qWarning() << "Refresh Hue Light request error:" << status << reply->errorString(); + reply->deleteLater(); + return; + } + + QByteArray data = reply->readAll(); + processLightRefreshResponse(device, data); + + } else if (m_bridgeRefreshRequests.keys().contains(reply)) { + + Device *device = m_bridgeRefreshRequests.take(reply); + + // check HTTP status code + if (status != 200) { + qWarning() << "Refresh Hue Bridge request error:" << status << reply->errorString(); + reply->deleteLater(); + return; + } + + QByteArray data = reply->readAll(); + processBridgeRefreshResponse(device, data); + + } else if (m_asyncActions.keys().contains(reply)) { + + QPair actionInfo = m_asyncActions.take(reply); + + // check HTTP status code + if (status != 200) { + qWarning() << "Refresh Hue Light request error:" << status << reply->errorString(); + reply->deleteLater(); + return; + } + + QByteArray data = reply->readAll(); + processActionResponse(actionInfo.first, actionInfo.second, data); + } + + reply->deleteLater(); +} + void DevicePluginPhilipsHue::guhTimer() { - foreach (Light *light, m_lights.keys()) { - light->refresh(); + foreach (Device *device, m_bridges.values()) { + refreshBridge(device); } } DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const Action &action) { - Light *light = m_lights.key(device); - if (!light) { - return DeviceManager::DeviceErrorDeviceNotFound; + if (device->deviceClassId() == hueLightDeviceClassId) { + HueLight *light = m_lights.key(device); + + if (!light->reachable()) { + return DeviceManager::DeviceErrorHardwareNotAvailable; + } + + if (action.actionTypeId() == huePowerActionTypeId) { + QPair request = light->createSetPowerRequest(action.param("power").value().toBool()); + m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id())); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == hueColorActionTypeId) { + QPair request = light->createSetColorRequest(action.param("color").value().value()); + m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id())); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == hueBrightnessActionTypeId) { + QPair request = light->createSetBrightnessRequest(percentageToBrightness(action.param("brightness").value().toInt())); + m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id())); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == hueEffectActionTypeId) { + QPair request = light->createSetEffectRequest(action.param("effect").value().toString()); + m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id())); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == hueAlertActionTypeId) { + QPair request = light->createFlashRequest(action.param("alert").value().toString()); + m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id())); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == hueTemperatureActionTypeId) { + QPair request = light->createSetTemperatureRequest(action.param("color temperature").value().toInt()); + m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id())); + return DeviceManager::DeviceErrorAsync; + } + return DeviceManager::DeviceErrorActionTypeNotFound; } - if (!light->reachable()) { - qWarning() << "Hue Bulb not reachable"; - return DeviceManager::DeviceErrorHardwareNotAvailable; - } + if (device->deviceClassId() == hueBridgeDeviceClassId) { - if (action.actionTypeId() == hueColorActionTypeId) { - light->setColor(action.param("color").value().value()); - } else if (action.actionTypeId() == huePowerActionTypeId) { - light->setOn(action.param("power").value().toBool()); - } else if (action.actionTypeId() == hueBrightnessActionTypeId) { - light->setBri(percentageToBrightness(action.param("brightness").value().toInt())); + + + + return DeviceManager::DeviceErrorActionTypeNotFound; } - return DeviceManager::DeviceErrorNoError; + return DeviceManager::DeviceErrorDeviceClassNotFound; } - -void DevicePluginPhilipsHue::createUserFinished(int id, const QVariant &response) +void DevicePluginPhilipsHue::lightStateChanged() { - qDebug() << "createuser response" << response; + HueLight *light = static_cast(sender()); - PairingInfo pairingInfo = m_pairings.take(id); - if (response.toMap().contains("error")) { - qDebug() << "Failed to pair Hue bridge:" << response.toMap().value("error").toMap().value("description"); + Device *device = m_lights.value(light); + if (!device) + return; + + device->setStateValue(hueReachableStateTypeId, light->reachable()); + device->setStateValue(hueColorStateTypeId, QVariant::fromValue(light->color())); + device->setStateValue(huePowerStateTypeId, light->power()); + device->setStateValue(hueBrightnessStateTypeId, brightnessToPercentage(light->brightness())); + device->setStateValue(hueTemperatureStateTypeId, light->ct()); + device->setStateValue(hueEffectStateTypeId, light->effect()); + device->setStateValue(hueAlertStateTypeId, light->alert()); +} + +void DevicePluginPhilipsHue::refreshLight(Device *device) +{ + HueLight *light = m_lights.key(device); + + QNetworkRequest request(QUrl("http://" + light->hostAddress().toString() + "/api/" + light->username() + "/lights/" + QString::number(light->lightId()))); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + QNetworkReply *reply = networkManagerGet(request); + + m_lightRefreshRequests.insert(reply, device); +} + +void DevicePluginPhilipsHue::refreshBridge(Device *device) +{ + HueBridge *bridge = m_bridges.key(device); + + QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->username() + "/lights/")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + QNetworkReply *reply = networkManagerGet(request); + + m_bridgeRefreshRequests.insert(reply, device); +} + +void DevicePluginPhilipsHue::processLightRefreshResponse(Device *device, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qWarning() << "Hue Bridge json error in response" << error.errorString(); + return; + } + + // check pairing error + if (data.contains("error")) { + qWarning() << "Failed to refresh Hue Light:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + return; + } + + HueLight *hueLight = m_lights.key(device); + hueLight->setStates(jsonDoc.toVariant().toMap().value("state").toMap()); +} + +void DevicePluginPhilipsHue::processBridgeRefreshResponse(Device *device, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qWarning() << "Hue Bridge json error in response" << error.errorString(); + return; + } + + // check pairing error + if (data.contains("error")) { + qWarning() << "Failed to refresh Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + return; + } + + QVariantMap lightsMap = jsonDoc.toVariant().toMap(); + foreach (const QString &lightId, lightsMap.keys()) { + QVariantMap lightMap = lightsMap.value(lightId).toMap(); + // get the light of this bridge + foreach (HueLight *light, m_lights.keys()) { + if (light->lightId() == lightId.toInt() && light->bridgeId() == device->id()) { + light->setStates(lightMap.value("state").toMap()); + } + } + } +} + +void DevicePluginPhilipsHue::processPairingResponse(const DevicePluginPhilipsHue::PairingInfo &pairingInfo, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qWarning() << "Hue Bridge json error in response" << error.errorString(); emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure); return; } - // 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 QVariant ¶ms) -{ - qDebug() << "getlightsfinished" << params; - PairingInfo pairingInfo = m_pairings.take(id); - - if (params.toMap().count() == 0) { - qWarning() << "No light bulbs found on this hue bridge... Cannot proceed with pairing."; + // check pairing error + if (data.contains("error")) { + qWarning() << "Failed to pair Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure); return; } - // Store a list of all known Lights - foreach (const QString &lightId, params.toMap().keys()) { - Light *light = new Light(QHostAddress(pairingInfo.ipParam.value().toString()), pairingInfo.usernameParam.value().toString(), lightId.toInt(), this); - m_unconfiguredLights.insert(lightId.toInt(), light); + // Paired successfully, check bridge/light information + QNetworkRequest request(QUrl("http://" + pairingInfo.host.toString() + "/api/" + pairingInfo.username + "")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + QNetworkReply *reply = networkManagerGet(request); + + m_informationRequests.insert(reply, pairingInfo); +} + +void DevicePluginPhilipsHue::processInformationResponse(const DevicePluginPhilipsHue::PairingInfo &pairingInfo, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qWarning() << "Hue Bridge json error in response" << error.errorString(); + emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure); + return; + } + + QVariantMap response = jsonDoc.toVariant().toMap(); + + // check json error + if (response.contains("error")) { + qWarning() << "Failed to get information from Hue Bridge:" << response.value("error").toMap().value("description").toString(); + emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure); + return; + } + + // create Bridge + HueBridge *bridge = new HueBridge(pairingInfo.username, pairingInfo.host); + + bridge->setApiVersion(response.value("config").toMap().value("apiversion").toString()); + bridge->setMacAddress(response.value("config").toMap().value("mac").toString()); + bridge->setName(response.value("config").toMap().value("name").toString()); + bridge->setZigbeeChannel(response.value("config").toMap().value("zigbeechannel").toInt()); + + m_unconfiguredBridges.append(bridge); + + // create Lights + QVariantMap lightsMap = response.value("lights").toMap(); + foreach (QString lightId, lightsMap.keys()) { + QVariantMap lightMap = lightsMap.value(lightId).toMap(); + HueLight *hueLight = new HueLight(lightId.toInt(), + bridge->hostAddress(), + lightMap.value("name").toString(), + pairingInfo.username, + lightMap.value("modelid").toString(), + DeviceId(), + this); + + hueLight->setStates(lightMap.value("state").toMap()); + + bridge->addLight(hueLight); + m_unconfiguredLights.append(hueLight); + + connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); } emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess); } -void DevicePluginPhilipsHue::getFinished(int id, const QVariant ¶ms) +void DevicePluginPhilipsHue::processActionResponse(Device *device, const ActionId actionId, const QByteArray &data) { - qDebug() << "got lights" << params << id; -} + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); -void DevicePluginPhilipsHue::lightStateChanged() -{ - Light *light = static_cast(sender()); - - Device *device; - if (m_asyncSetups.contains(light)) { - device = m_asyncSetups.take(light); - device->setName(light->name()); - device->setParamValue("name", light->name()); - emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess); - } else { - device = m_lights.value(light); - } - if (!device) { + // check JSON error + if (error.error != QJsonParseError::NoError) { + qWarning() << "Hue Bridge json error in response" << error.errorString(); + emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable); return; } - device->setStateValue(hueReachableStateTypeId, light->reachable()); - device->setStateValue(hueColorStateTypeId, QVariant::fromValue(light->color())); - device->setStateValue(huePowerStateTypeId, light->on()); - device->setStateValue(hueBrightnessStateTypeId, brightnessToPercentage(light->bri())); + + // check pairing error + if (data.contains("error")) { + qWarning() << "Failed to execute Hue action:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable); + return; + } + + m_lights.key(device)->processActionResponse(jsonDoc.toVariant().toList()); + emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); } int DevicePluginPhilipsHue::brightnessToPercentage(int brightness) diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.h b/plugins/deviceplugins/philipshue/devicepluginphilipshue.h index de6be85a..61859395 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.h +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.h @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * * * * This file is part of guh. * * * @@ -22,8 +23,8 @@ #define DEVICEPLUGINPHILIPSHUE_H #include "plugin/deviceplugin.h" -#include "huebridgeconnection.h" -#include "light.h" +#include "huebridge.h" +#include "huelight.h" class QNetworkReply; @@ -38,28 +39,21 @@ public: explicit DevicePluginPhilipsHue(); DeviceManager::HardwareResources requiredHardware() const override; - - void startMonitoringAutoDevices() override; - - QList configurationDescription() const override; - DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; - DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; + DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; void deviceRemoved(Device *device) override; void upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) override; DeviceManager::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + void networkManagerReplyReady(QNetworkReply *reply) override; + void guhTimer() override; public slots: DeviceManager::DeviceError executeAction(Device *device, const Action &action); private slots: - void createUserFinished(int id, const QVariant ¶ms); - void getLightsFinished(int id, const QVariant ¶ms); - void getFinished(int id, const QVariant ¶ms); - void lightStateChanged(); private: @@ -67,17 +61,31 @@ private: class PairingInfo { public: PairingTransactionId pairingTransactionId; - Param ipParam; - Param usernameParam; + QHostAddress host; + QString username; }; - QHash m_pairings; - HueBridgeConnection *m_bridge; + QHash m_pairingRequests; + QHash m_informationRequests; - QList m_unconfiguredLights; - QHash m_lights; + QList m_unconfiguredBridges; + QList m_unconfiguredLights; - QHash m_asyncSetups; + QHash m_lightRefreshRequests; + QHash m_bridgeRefreshRequests; + QHash > m_asyncActions; + + QHash m_bridges; + QHash m_lights; + + void refreshLight(Device *device); + void refreshBridge(Device *device); + + void processLightRefreshResponse(Device *device, const QByteArray &data); + void processBridgeRefreshResponse(Device *device, const QByteArray &data); + void processPairingResponse(const PairingInfo &pairingInfo, const QByteArray &data); + void processInformationResponse(const PairingInfo &pairingInfo, const QByteArray &data); + void processActionResponse(Device *device, const ActionId actionId, const QByteArray &data); int brightnessToPercentage(int brightness); int percentageToBrightness(int percentage); diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json index f2a549d1..ccdc0358 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json @@ -7,12 +7,12 @@ "name": "Philips", "deviceClasses": [ { - "deviceClassId": "d8f4c397-e05e-47c1-8917-8e72d4d0d47c", - "idName": "hue", - "name": "Hue", - "createMethods": ["discovery", "auto"], + "deviceClassId": "642aa4c7-19aa-45ed-ba06-aa1ae6c9edf7", + "idName": "hueBridge", + "name": "Hue Bridge", + "createMethods": ["discovery"], "setupMethod": "pushButton", - "pairingInfo": "Please press the button on the Hue bridge and then press OK", + "pairingInfo": "Please press the button on the Hue Bridge before you continue", "paramTypes": [ { "name": "name", @@ -20,11 +20,71 @@ "inputType": "TextLine" }, { - "name": "ip", + "name": "username", + "type" : "QString", + "inputType": "TextLine", + "readOnly": true + }, + { + "name": "host address", "type" : "QString", "inputType": "IPv4Address", "readOnly": true }, + { + "name": "mac address", + "type" : "QString", + "inputType": "MacAddress", + "readOnly": true + }, + { + "name": "api version", + "type" : "QString", + "readOnly": true + }, + { + "name": "zigbee channel", + "type" : "int", + "readOnly": true + } + ], + "stateTypes": [ + { + "id": "15794d26-fde8-4a61-8f83-d7830534975f", + "idName": "bridgeReachable", + "name": "reachable", + "type": "bool" + } + ], + "actionTypes": [ + { + "id": "001476ce-2f17-475f-939f-d4234751ef35", + "idName": "searchLamps", + "name": "search new lamps" + } + ] + }, + { + "deviceClassId": "0edba26c-96ab-44fb-a6a2-c0574d19630e", + "idName": "hueLight", + "name": "Hue Light", + "createMethods": ["auto"], + "paramTypes": [ + { + "name": "name", + "type" : "QString", + "inputType": "TextLine" + }, + { + "name": "bridge", + "type" : "QString", + "readOnly": true + }, + { + "name": "model id", + "type" : "QString", + "readOnly": true + }, { "name": "username", "type" : "QString", @@ -32,18 +92,43 @@ "readOnly": true }, { - "name": "lightId", + "name": "host address", + "type" : "QString", + "inputType": "IPv4Address", + "readOnly": true + }, + { + "name": "light id", "type" : "int", "readOnly": true } ], "stateTypes": [ { - "id": "15794d26-fde8-4a61-8f83-d7830534975f", + "id": "19bb8d10-1b28-4ba3-99b7-a634138dcfde", "idName": "hueReachable", "name": "reachable", "type": "bool" }, + { + "id": "90aaffe5-6a76-47d2-a14a-550f60390245", + "idName": "huePower", + "name": "power", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "c0f4206f-f219-4f06-93c4-4ca515a56f79", + "idName": "hueTemperature", + "name": "color temperature", + "type": "int", + "unit": "Mired", + "writable": true, + "defaultValue": 170, + "minValue": 154, + "maxValue": 500 + }, { "id": "d25423e7-b924-4b20-80b6-77eecc65d089", "idName": "hueColor", @@ -53,24 +138,41 @@ "writable": true }, - { - "id": "90aaffe5-6a76-47d2-a14a-550f60390245", - "idName": "huePower", - "name": "power", - "type": "bool", - "defaultValue": false, - "writable": true - }, { "id": "90e91f64-a208-468c-a5a2-7f47e08859e2", "idName": "hueBrightness", "name": "brightness", "type": "int", + "unit": "Percentage", "minValue": 0, "maxValue": 100, - "unit": "Percentage", "defaultValue": 0, "writable": true + }, + { + "id": "0b7cdd8d-4db8-4183-abe2-f3c01d1c9afc", + "idName": "hueEffect", + "name": "effect", + "type": "QString", + "defaultValue": "none", + "allowedValues": [ + "none", + "color loop" + ], + "writable": true + }, + { + "id": "acd09e71-3305-451c-910a-bc2d6e1d5144", + "idName": "hueAlert", + "name": "alert", + "type": "QString", + "defaultValue": "none", + "allowedValues": [ + "none", + "flash", + "flash 30 seconds" + ], + "writable": true } ] } diff --git a/plugins/deviceplugins/philipshue/huebridge.cpp b/plugins/deviceplugins/philipshue/huebridge.cpp new file mode 100644 index 00000000..d934b6d7 --- /dev/null +++ b/plugins/deviceplugins/philipshue/huebridge.cpp @@ -0,0 +1,103 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "huebridge.h" + +HueBridge::HueBridge(QString username, QHostAddress hostAddress, QObject *parent) : + QObject(parent), + m_username(username), + m_hostAddress(hostAddress), + m_name(QString()), + m_macAddress(QString()), + m_apiVersion(QString()), + m_zigbeeChannel(-1) +{ + +} + +QString HueBridge::name() const +{ + return m_name; +} + +void HueBridge::setName(const QString &name) +{ + m_name = name; +} + +QString HueBridge::username() const +{ + return m_username; +} + +void HueBridge::setUsername(const QString &username) +{ + m_username = username; +} + +QHostAddress HueBridge::hostAddress() const +{ + return m_hostAddress; +} + +void HueBridge::setHostAddress(const QHostAddress &hostAddress) +{ + m_hostAddress = hostAddress; +} + +QString HueBridge::macAddress() const +{ + return m_macAddress; +} + +void HueBridge::setMacAddress(const QString &macAddress) +{ + m_macAddress = macAddress; +} + +QString HueBridge::apiVersion() const +{ + return m_apiVersion; +} + +void HueBridge::setApiVersion(const QString &apiVersion) +{ + m_apiVersion = apiVersion; +} + +int HueBridge::zigbeeChannel() const +{ + return m_zigbeeChannel; +} + +void HueBridge::setZigbeeChannel(const int &zigbeeChannel) +{ + m_zigbeeChannel = zigbeeChannel; +} + +QList HueBridge::lights() const +{ + return m_lights; +} + +void HueBridge::addLight(HueLight *light) +{ + m_lights.append(light); +} diff --git a/plugins/deviceplugins/philipshue/huebridgeconnection.h b/plugins/deviceplugins/philipshue/huebridge.h similarity index 57% rename from plugins/deviceplugins/philipshue/huebridgeconnection.h rename to plugins/deviceplugins/philipshue/huebridge.h index 4d58a967..472fd793 100644 --- a/plugins/deviceplugins/philipshue/huebridgeconnection.h +++ b/plugins/deviceplugins/philipshue/huebridge.h @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * * * * This file is part of guh. * * * @@ -18,46 +18,55 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef HUEBRIDGECONNECTION_H -#define HUEBRIDGECONNECTION_H +#ifndef HUEBRIDGE_H +#define HUEBRIDGE_H #include #include -#include -#include -class Caller -{ -public: - QPointer obj; - QString method; - int id; -}; +#include "huelight.h" -class HueBridgeConnection : public QObject +class HueBridge : public QObject { Q_OBJECT public: - explicit HueBridgeConnection(QObject *parent = 0); + explicit HueBridge(QString username, QHostAddress hostAddress, QObject *parent = 0); - int createUser(const QHostAddress &address, const QString &username); + QString name() const; + void setName(const QString &name); - 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); + QString username() const; + void setUsername(const QString &username); -private slots: - void slotCreateUserFinished(); - void slotGetFinished(); + QHostAddress hostAddress() const; + void setHostAddress(const QHostAddress &hostAddress); -signals: - void createUserFinished(int id, const QVariantMap &map); - void getFinished(int id, const QVariantMap &map); + QString macAddress() const; + void setMacAddress(const QString &macAddress); + + QString apiVersion() const; + void setApiVersion(const QString &apiVersion); + + int zigbeeChannel() const; + void setZigbeeChannel(const int &zigbeeChannel); + + QList lights() const; + void addLight(HueLight *light); private: - QNetworkAccessManager *m_nam; - int m_requestCounter; - QHash m_createUserMap; - QHash m_requestMap; + QString m_username; + QHostAddress m_hostAddress; + QString m_name; + QString m_macAddress; + QString m_apiVersion; + int m_zigbeeChannel; + + QList m_lights; +signals: + + +public slots: + }; -#endif // HUEBRIDGECONNECTION_H +#endif // HUEBRIDGE_H diff --git a/plugins/deviceplugins/philipshue/huebridgeconnection.cpp b/plugins/deviceplugins/philipshue/huebridgeconnection.cpp deleted file mode 100644 index a070233b..00000000 --- a/plugins/deviceplugins/philipshue/huebridgeconnection.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2013 Michael Zanetti * - * * - * 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 . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "huebridgeconnection.h" - -#include -#include -#include -#include - -HueBridgeConnection::HueBridgeConnection(QObject *parent) : - QObject(parent) -{ - m_nam = new QNetworkAccessManager(this); -} - -int HueBridgeConnection::createUser(const QHostAddress &address, const QString &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++; -} - -int HueBridgeConnection::get(const QHostAddress &address, const QString &username, const QString &path, QObject *caller, const QString &methodName) -{ - 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(sender()); - QByteArray data = reply->readAll(); - int id = m_createUserMap.take(reply); - - reply->deleteLater(); - - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - - qDebug() << jsonDoc.toJson(); - - 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(sender()); - QByteArray data = reply->readAll(); - Caller c = m_requestMap.take(reply); - - reply->deleteLater(); - - 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; - } - - if (jsonDoc.toJson().contains("error")){ - qDebug() << jsonDoc.toJson(); - } - - QVariant response = jsonDoc.toVariant(); - emit getFinished(c.id, response.toMap()); - if (c.obj) { - metaObject()->invokeMethod(c.obj.data(), c.method.toLatin1().data(), Q_ARG(int, c.id), Q_ARG(QVariant, response)); - } -} diff --git a/plugins/deviceplugins/philipshue/huelight.cpp b/plugins/deviceplugins/philipshue/huelight.cpp new file mode 100644 index 00000000..8dfae998 --- /dev/null +++ b/plugins/deviceplugins/philipshue/huelight.cpp @@ -0,0 +1,397 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "huelight.h" + +HueLight::HueLight(const int &lightId, const QHostAddress &hostAddress, const QString &name, const QString &username, const QString &modelId, const DeviceId &bridgeId, QObject *parent) : + QObject(parent), + m_lightId(lightId), + m_hostAddress(hostAddress), + m_name(name), + m_username(username), + m_modelId(modelId), + m_bridgeId(bridgeId) +{ +} + +int HueLight::lightId() const +{ + return m_lightId; +} + +void HueLight::setLightId(const int &lightId) +{ + m_lightId = lightId; +} + +DeviceId HueLight::bridgeId() const +{ + return m_bridgeId; +} + +void HueLight::setBridgeId(const DeviceId &bridgeDeviceId) +{ + m_bridgeId = bridgeDeviceId; +} + +QHostAddress HueLight::hostAddress() const +{ + return m_hostAddress; +} + +void HueLight::setHostAddress(const QHostAddress hostAddress) +{ + m_hostAddress = hostAddress; +} + +QString HueLight::name() const +{ + return m_name; +} + +void HueLight::setName(const QString &name) +{ + m_name = name; +} + +QString HueLight::username() const +{ + return m_username; +} + +void HueLight::setUsername(const QString &username) +{ + m_username = username; +} + +QString HueLight::modelId() const +{ + return m_modelId; +} + +void HueLight::setModelId(const QString &modelId) +{ + m_modelId = modelId; +} + +QString HueLight::type() const +{ + return m_type; +} + +void HueLight::setType(const QString &type) +{ + m_type = type; +} + +QString HueLight::softwareVersion() const +{ + return m_softwareVersion; +} + +void HueLight::setSoftwareVersion(const QString &softwareVersion) +{ + m_softwareVersion = softwareVersion; +} + +bool HueLight::power() const +{ + return m_power; +} + +void HueLight::setPower(const bool &power) +{ + m_power = power; +} + +bool HueLight::reachable() const +{ + return m_reachable; +} + +void HueLight::setReachable(const bool &reachable) +{ + m_reachable = reachable; +} + +quint8 HueLight::brightness() const +{ + return m_brightness; +} + +void HueLight::setBrigtness(const quint8 brightness) +{ + m_brightness = brightness; +} + +quint16 HueLight::hue() const +{ + return m_hue; +} + +void HueLight::setHue(const quint16 hue) +{ + m_hue = hue; +} + +quint8 HueLight::sat() const +{ + return m_sat; +} + +void HueLight::setSat(const quint8 sat) +{ + m_sat = sat; +} + +QColor HueLight::color() const +{ + return QColor::fromHsv(m_hue * 360 / 65535, m_sat, 255); +} + +QPointF HueLight::xy() const +{ + return m_xy; +} + +void HueLight::setXy(const QPointF &xy) +{ + m_xy = xy; +} + +quint16 HueLight::ct() const +{ + return m_ct; +} + +void HueLight::setCt(const quint16 &ct) +{ + m_ct = ct; +} + +QString HueLight::alert() const +{ + return m_alert; +} + +void HueLight::setAlert(const QString &alert) +{ + m_alert = alert; +} + +QString HueLight::effect() const +{ + return m_effect; +} + +void HueLight::setEffect(const QString &effect) +{ + m_effect = effect; +} + +HueLight::ColorMode HueLight::colorMode() const +{ + return m_colorMode; +} + +void HueLight::setColorMode(const HueLight::ColorMode &colorMode) +{ + m_colorMode = colorMode; +} + +void HueLight::setStates(const QVariantMap &statesMap) +{ + // color mode + if (statesMap.value("colormode").toString() == "hs") { + setColorMode(ColorModeHS); + } else if (statesMap.value("colormode").toString() == "ct") { + setColorMode(ColorModeCT); + } else if (statesMap.value("colormode").toString() == "xy") { + setColorMode(ColorModeXY); + } + + // effect (none, colorloop) + if (statesMap.value("effect").toString() == "none") { + setEffect("none"); + } else if (statesMap.value("effect").toString() == "colorloop") { + setEffect("color loop"); + } + + // alert (none, select, lselect) + setAlert(statesMap.value("alert").toString()); + + setBrigtness(statesMap.value("bri").toInt()); + setCt(statesMap.value("ct").toInt()); + setPower(statesMap.value("on").toBool()); + setReachable(statesMap.value("reachable").toBool()); + setSat(statesMap.value("sat").toInt()); + setHue(statesMap.value("hue").toInt()); + setXy(QPointF(statesMap.value("xy").toList().first().toFloat(),statesMap.value("xy").toList().last().toFloat())); + + emit stateChanged(); +} + +void HueLight::processActionResponse(const QVariantList &responseList) +{ + foreach (const QVariant &resultVariant, responseList) { + QVariantMap result = resultVariant.toMap(); + if (result.contains("success")) { + QVariantMap successMap = result.value("success").toMap(); + if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/on")) { + m_power = successMap.value("/lights/" + QString::number(m_lightId) + "/state/on").toBool(); + } + if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/hue")) { + m_hue = successMap.value("/lights/" + QString::number(m_lightId) + "/state/hue").toInt(); + m_colorMode = ColorModeHS; + } + if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/bri")) { + m_brightness = successMap.value("/lights/" + QString::number(m_lightId) + "/state/bri").toInt(); + } + if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/sat")) { + m_sat = successMap.value("/lights/" + QString::number(m_lightId) + "/state/sat").toInt(); + m_colorMode = ColorModeHS; + } + if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/xy")) { + m_xy = successMap.value("/lights/" + QString::number(m_lightId) + "/state/xy").toPoint(); + m_colorMode = ColorModeXY; + } + if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/ct")) { + m_ct = successMap.value("/lights/" + QString::number(m_lightId) + "/state/ct").toInt(); + m_colorMode = ColorModeCT; + } + if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/effect")) { + QString effect = successMap.value("/lights/" + QString::number(m_lightId) + "/state/effect").toString(); + if (effect == "none") { + setEffect("none"); + } else if (effect == "colorloop") { + setEffect("color loop"); + } + } + if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/alert")) { + QString alert = successMap.value("/lights/" + QString::number(m_lightId) + "/state/alert").toString(); + if (alert == "none") { + m_alert = "none"; + } else if (alert == "select") { + m_alert = "flash"; + } else if (alert == "lselect") { + m_alert = "flash 30 seconds"; + } + } + + } + } + emit stateChanged(); +} + +QPair HueLight::createSetPowerRequest(const bool &power) +{ + QVariantMap requestMap; + requestMap.insert("on", power); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); + + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() + + "/lights/" + QString::number(lightId()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + return QPair(request, jsonDoc.toJson()); +} + +QPair HueLight::createSetColorRequest(const QColor &color) +{ + QVariantMap requestMap; + requestMap.insert("hue", color.hue() * 65535 / 360); + requestMap.insert("sat", color.saturation()); + requestMap.insert("on", true); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); + + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() + + "/lights/" + QString::number(lightId()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + return QPair(request, jsonDoc.toJson()); +} + +QPair HueLight::createSetBrightnessRequest(const int &brightness) +{ + QVariantMap requestMap; + requestMap.insert("bri", brightness); + if (brightness == 0) { + requestMap.insert("on", false); + } else { + requestMap.insert("on", true); + } + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); + + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() + + "/lights/" + QString::number(lightId()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + return QPair(request, jsonDoc.toJson()); +} + +QPair HueLight::createSetEffectRequest(const QString &effect) +{ + QVariantMap requestMap; + if (effect == "none") { + requestMap.insert("effect", "none"); + } else if (effect == "color loop") { + requestMap.insert("effect", "colorloop"); + requestMap.insert("on", true); + } + QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); + + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() + + "/lights/" + QString::number(lightId()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + return QPair(request, jsonDoc.toJson()); +} + +QPair HueLight::createSetTemperatureRequest(const int &colorTemp) +{ + QVariantMap requestMap; + requestMap.insert("ct", colorTemp); + requestMap.insert("on", true); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); + + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() + + "/lights/" + QString::number(lightId()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + return QPair(request, jsonDoc.toJson()); +} + +QPair HueLight::createFlashRequest(const QString &alert) +{ + QVariantMap requestMap; + if (alert == "none") { + requestMap.insert("alert", "none"); + } else if (alert == "flash") { + requestMap.insert("alert", "select"); + } else if (alert == "flash 30 seconds") { + requestMap.insert("alert", "lselect"); + } + QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); + + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() + + "/lights/" + QString::number(lightId()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + return QPair(request, jsonDoc.toJson()); +} diff --git a/plugins/deviceplugins/philipshue/light.h b/plugins/deviceplugins/philipshue/huelight.h similarity index 53% rename from plugins/deviceplugins/philipshue/light.h rename to plugins/deviceplugins/philipshue/huelight.h index 70ea0555..5236449f 100644 --- a/plugins/deviceplugins/philipshue/light.h +++ b/plugins/deviceplugins/philipshue/huelight.h @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright (C) 2013 Michael Zanetti * + * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * * * * This file is part of guh. * * * @@ -18,122 +19,128 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef LIGHT_H -#define LIGHT_H +#ifndef HUELIGHT_H +#define HUELIGHT_H #include -#include +#include #include +#include #include -#include +#include +#include -class HueBridgeConnection; +#include "typeutils.h" +#include "types/action.h" -class Light: public QObject +class HueLight : public QObject { Q_OBJECT public: + enum ColorMode { ColorModeHS, ColorModeXY, ColorModeCT }; - Light(const QHostAddress &ip, const QString &username, int id, QObject *parent = 0); + explicit HueLight(const int &lightId, const QHostAddress &hostAddress, const QString &name, const QString &username, const QString &modelId, const DeviceId &bridgeId, QObject *parent = 0); - QHostAddress ip() const; - QString username() const; + int lightId() const; + void setLightId(const int &lightId); - int id() const; + DeviceId bridgeId() const; + void setBridgeId(const DeviceId &bridgeDeviceId); + + QHostAddress hostAddress() const; + void setHostAddress(const QHostAddress hostAddress); QString name() const; void setName(const QString &name); + QString username() const; + void setUsername(const QString &username); + QString modelId() const; void setModelId(const QString &modelId); QString type() const; void setType(const QString &type); - QString swversion() const; - void setSwversion(const QString &swversion); + QString softwareVersion() const; + void setSoftwareVersion(const QString &softwareVersion); + + bool power() const; + void setPower(const bool &power); - // 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; + void setReachable(const bool &reachable); + quint8 brightness() const; + void setBrigtness(const quint8 brightness); -public slots: - void refresh(); + quint16 hue() const; + void setHue(const quint16 hue); - void setOn(bool on); - void setBri(quint8 bri); - void setHue(quint16 hue); - void setSat(quint8 sat); - void setColor(const QColor &color); + quint8 sat() const; + void setSat(const quint8 sat); + + QColor color() const; + + QPointF xy() const; void setXy(const QPointF &xy); - void setCt(quint16 ct); + + quint16 ct() const; + void setCt(const quint16 &ct); + + QString alert() const; void setAlert(const QString &alert); + + QString effect() const; void setEffect(const QString &effect); -signals: - void stateChanged(); + ColorMode colorMode() const; + void setColorMode(const ColorMode &colorMode); -private slots: - void responseReceived(int id, const QVariant &response); - void setDescriptionFinished(int id, const QVariant &response); - void setStateFinished(int id, const QVariant &response); + // update states + void setStates(const QVariantMap &statesMap); + void processActionResponse(const QVariantList &responseList); + + // create action requests + QPair createSetPowerRequest(const bool &power); + QPair createSetColorRequest(const QColor &color); + QPair createSetBrightnessRequest(const int &brightness); + QPair createSetEffectRequest(const QString &effect); + QPair createSetTemperatureRequest(const int &colorTemp); + QPair createFlashRequest(const QString &alert); private: - void setReachable(bool reachable); - - HueBridgeConnection *m_bridge; - QHostAddress m_ip; - QString m_username; - - int m_id; + int m_lightId; + QHostAddress m_hostAddress; QString m_name; - + QString m_username; QString m_modelId; + DeviceId m_bridgeId; QString m_type; - QString m_swversion; + QString m_softwareVersion; - bool m_on; - quint8 m_bri; + bool m_power; + bool m_reachable; + + quint8 m_brightness; quint16 m_hue; quint8 m_sat; QPointF m_xy; quint16 m_ct; QString m_alert; QString m_effect; - ColorMode m_colormode; - bool m_reachable; + ColorMode m_colorMode; - 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; +signals: + void stateChanged(); + +public slots: - // FIXME: This is needed as sometimes we don't get a reply from the bridge - // Can't use guhtimer right now as that triggers in intervals that aren't - // related to our sending. Perhaps we should create a guhtimeout thing? - QTimer m_busyTimeout; }; -#endif +#endif // HUELIGHT_H diff --git a/plugins/deviceplugins/philipshue/light.cpp b/plugins/deviceplugins/philipshue/light.cpp deleted file mode 100644 index d513da33..00000000 --- a/plugins/deviceplugins/philipshue/light.cpp +++ /dev/null @@ -1,421 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2013 Michael Zanetti * - * * - * 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 . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - -#include "light.h" -#include "huebridgeconnection.h" - -#include -#include -#include - -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) -{ - m_busyTimeout.setSingleShot(true); - m_busyTimeout.setInterval(2000); - connect(&m_busyTimeout, &QTimer::timeout, [this]{ setStateFinished(m_busyStateChangeId, QVariant()); }); -} - -QHostAddress Light::ip() const -{ - return m_ip; -} - -QString Light::username() const -{ - return m_username; -} - -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; - if (bri == 0) { - params.insert("bri", bri); - params.insert("on", false); - } else { - 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"); - m_busyTimeout.start(); - } 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(); - - 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; - - params.insert("on", true); - m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished"); - m_busyTimeout.start(); - } else { - m_dirtyHue = hue; - m_hueDirty = true; - m_dirtySat = sat; - m_satDirty = true; - } -} - -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"); - m_busyTimeout.start(); - } 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_busyTimeout.stop(); - 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"); - m_busyTimeout.start(); - } 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"); - m_busyTimeout.start(); - } 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"); - m_busyTimeout.start(); - } - } -} diff --git a/plugins/deviceplugins/philipshue/philipshue.pro b/plugins/deviceplugins/philipshue/philipshue.pro index 93f81faa..7bf16e28 100644 --- a/plugins/deviceplugins/philipshue/philipshue.pro +++ b/plugins/deviceplugins/philipshue/philipshue.pro @@ -6,14 +6,18 @@ QT += network SOURCES += \ devicepluginphilipshue.cpp \ - huebridgeconnection.cpp \ - light.cpp + #huebridgeconnection.cpp \ + #light.cpp \ + huebridge.cpp \ + huelight.cpp HEADERS += \ devicepluginphilipshue.h \ - huebridgeconnection.h \ - light.h \ - lightinterface.h + #huebridgeconnection.h \ + #light.h \ + #lightinterface.h \ + huebridge.h \ + huelight.h diff --git a/tests/auto/api.json b/tests/auto/api.json index b65145d0..8f973f8f 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -719,6 +719,7 @@ "UnitRadiant", "UnitDegreeCelsius", "UnitDegreeKelvin", + "UnitMired", "UnitMilliBar", "UnitBar", "UnitPascal",