From 1dae30eb80385bad554cb4fc075f5364e413ddc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 17 Dec 2015 22:38:48 +0100 Subject: [PATCH] rewrite hue plugin --- libguh/plugin/device.cpp | 4 +- .../philipshue/devicepluginphilipshue.cpp | 695 ++++++++++++++---- .../philipshue/devicepluginphilipshue.h | 39 +- .../philipshue/devicepluginphilipshue.json | 242 +++++- .../deviceplugins/philipshue/huebridge.cpp | 56 +- plugins/deviceplugins/philipshue/huebridge.h | 11 +- .../deviceplugins/philipshue/huedevice.cpp | 108 +++ plugins/deviceplugins/philipshue/huedevice.h | 62 ++ plugins/deviceplugins/philipshue/huelight.cpp | 159 +--- plugins/deviceplugins/philipshue/huelight.h | 43 +- .../deviceplugins/philipshue/hueremote.cpp | 43 ++ plugins/deviceplugins/philipshue/hueremote.h | 56 ++ .../deviceplugins/philipshue/philipshue.pro | 8 +- server/jsonrpc/devicehandler.cpp | 5 +- 14 files changed, 1205 insertions(+), 326 deletions(-) create mode 100644 plugins/deviceplugins/philipshue/huedevice.cpp create mode 100644 plugins/deviceplugins/philipshue/huedevice.h create mode 100644 plugins/deviceplugins/philipshue/hueremote.cpp create mode 100644 plugins/deviceplugins/philipshue/hueremote.h diff --git a/libguh/plugin/device.cpp b/libguh/plugin/device.cpp index 4bd35ba9..1ea574e6 100644 --- a/libguh/plugin/device.cpp +++ b/libguh/plugin/device.cpp @@ -191,7 +191,7 @@ void Device::setStateValue(const StateTypeId &stateTypeId, const QVariant &value // TODO: check min/max value + possible values - // to prevent an invalid state type + // to prevent an invalid state type from the plugin side State newState(stateTypeId, m_id); newState.setValue(value); @@ -200,7 +200,7 @@ void Device::setStateValue(const StateTypeId &stateTypeId, const QVariant &value return; } } - qCWarning(dcDeviceManager) << "failed setting state for" << m_name; + qCWarning(dcDeviceManager) << "failed setting state for" << m_name << value; } /*! Returns the \l{State} with the given \a stateTypeId of this Device. */ diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp b/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp index 166fdfec..4b077ffd 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp @@ -74,11 +74,16 @@ StateTypeId hueReachableStateTypeId = StateTypeId("15794d26-fde8-4a61-8f83-d7830 DevicePluginPhilipsHue::DevicePluginPhilipsHue() { + m_timer = new QTimer(this); + m_timer->setSingleShot(false); + m_timer->setInterval(1000); + + connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); } DeviceManager::HardwareResources DevicePluginPhilipsHue::requiredHardware() const { - return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery | DeviceManager::HardwareResourceNetworkManager; + return DeviceManager::HardwareResourceUpnpDisovery | DeviceManager::HardwareResourceNetworkManager; } DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) @@ -98,81 +103,78 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev foreach (HueBridge *b, m_unconfiguredBridges) { if (b->hostAddress().toString() == device->paramValue("host address").toString()) { m_unconfiguredBridges.removeAll(b); - + qCDebug(dcPhilipsHue) << "Setup unconfigured Hue Bridge" << b->name(); // set data which was not known during discovery device->setParamValue("name", b->name()); device->setParamValue("api key", b->apiKey()); device->setParamValue("zigbee channel", b->zigbeeChannel()); - device->setParamValue("api version", b->apiVersion()); - device->setParamValue("software version", b->softwareVersion()); + device->setParamValue("id", b->id()); 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("api key", light->apiKey())); - 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); - + device->setStateValue(bridgeReachableStateTypeId, true); + discoverBridgeDevices(b); + m_timer->start(); return DeviceManager::DeviceSetupStatusSuccess; } } // loaded bridge - HueBridge *bridge = new HueBridge(device->paramValue("api key").toString(), - QHostAddress(device->paramValue("host address").toString())); - + qCDebug(dcPhilipsHue) << "Setup Hue Bridge" << device->paramValue("name").toString(); + HueBridge *bridge = new HueBridge(this); + bridge->setId(device->paramValue("id").toString()); + bridge->setApiKey(device->paramValue("api key").toString()); + bridge->setHostAddress(QHostAddress(device->paramValue("host address").toString())); bridge->setName(device->paramValue("name").toString()); - bridge->setApiVersion(device->paramValue("api version").toString()); - bridge->setSoftwareVersion(device->paramValue("software version").toString()); bridge->setMacAddress(device->paramValue("mac address").toString()); bridge->setZigbeeChannel(device->paramValue("zigbee channel").toInt()); m_bridges.insert(bridge, device); + m_timer->start(); return DeviceManager::DeviceSetupStatusSuccess; } - // hue lights + // hue color light if (device->deviceClassId() == hueLightDeviceClassId) { + qCDebug(dcPhilipsHue) << "Setup Hue color light" << device->paramValue("name").toString(); + HueLight *hueLight = new HueLight(this); + hueLight->setId(device->paramValue("light id").toInt()); + hueLight->setHostAddress(QHostAddress(device->paramValue("host address").toString())); + hueLight->setName(device->paramValue("name").toString()); + hueLight->setApiKey(device->paramValue("api key").toString()); + hueLight->setModelId(device->paramValue("model id").toString()); + hueLight->setUuid(device->paramValue("uuid").toString()); + hueLight->setBridgeId(DeviceId(device->paramValue("bridge").toString())); - HueLight *hueLight = 0; - - // check if this is a unconfigured light - for (int i = 0; i < m_unconfiguredLights.count(); i++) { - if (m_unconfiguredLights.at(i)->apiKey() == device->paramValue("api key").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("api key").toString(), - device->paramValue("model id").toString(), - DeviceId(device->paramValue("bridge").toString()), - this); - connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); - } + connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); device->setParentId(device->paramValue("bridge").toString()); m_lights.insert(hueLight, device); + setLightName(device, device->paramValue("name").toString()); + return DeviceManager::DeviceSetupStatusSuccess; } - return DeviceManager::DeviceSetupStatusAsync; + // hue white light + if (device->deviceClassId() == hueWhiteLightDeviceClassId) { + qCDebug(dcPhilipsHue) << "Setup Hue white light" << device->paramValue("name").toString(); + + HueLight *hueLight = new HueLight(this); + hueLight->setId(device->paramValue("light id").toInt()); + hueLight->setHostAddress(QHostAddress(device->paramValue("host address").toString())); + hueLight->setName(device->paramValue("name").toString()); + hueLight->setApiKey(device->paramValue("api key").toString()); + hueLight->setModelId(device->paramValue("model id").toString()); + hueLight->setUuid(device->paramValue("uuid").toString()); + hueLight->setBridgeId(DeviceId(device->paramValue("bridge").toString())); + + connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); + device->setParentId(device->paramValue("bridge").toString()); + m_lights.insert(hueLight, device); + + setLightName(device, device->paramValue("name").toString()); + return DeviceManager::DeviceSetupStatusSuccess; + } + + return DeviceManager::DeviceSetupStatusFailure; } void DevicePluginPhilipsHue::deviceRemoved(Device *device) @@ -183,26 +185,36 @@ void DevicePluginPhilipsHue::deviceRemoved(Device *device) bridge->deleteLater(); } - if (device->deviceClassId() == hueLightDeviceClassId) { + if (device->deviceClassId() == hueLightDeviceClassId || device->deviceClassId() == hueWhiteLightDeviceClassId) { HueLight *light = m_lights.key(device); m_lights.remove(light); light->deleteLater(); } + + if (myDevices().isEmpty()) + m_timer->stop(); } void DevicePluginPhilipsHue::upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) { + if (upnpDeviceDescriptorList.isEmpty()) { + qCDebug(dcPhilipsHue) << "No UPnP device found. Try N-UPNP discovery."; + QNetworkRequest request(QUrl("https://www.meethue.com/api/nupnp")); + QNetworkReply *reply = networkManagerGet(request); + m_discoveryRequests.append(reply); + return; + } + QList deviceDescriptors; foreach (const UpnpDeviceDescriptor &upnpDevice, upnpDeviceDescriptorList) { if (upnpDevice.modelDescription().contains("Philips")) { DeviceDescriptor descriptor(hueBridgeDeviceClassId, "Philips Hue Bridge", upnpDevice.hostAddress().toString()); ParamList params; - params.append(Param("name", QString())); + params.append(Param("name", upnpDevice.friendlyName())); params.append(Param("host address", upnpDevice.hostAddress().toString())); params.append(Param("api key", QString())); params.append(Param("mac address", QString())); - params.append(Param("api version", QString())); - params.append(Param("software version", QString())); + params.append(Param("id", upnpDevice.serialNumber().toLower())); params.append(Param("zigbee channel", -1)); descriptor.setParams(params); deviceDescriptors.append(descriptor); @@ -267,8 +279,54 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) } processInformationResponse(pairingInfo, reply->readAll()); - } else if (m_lightRefreshRequests.keys().contains(reply)) { + } else if (m_discoveryRequests.contains(reply)) { + m_discoveryRequests.removeAll(reply); + // check HTTP status code + if (status != 200) { + qCWarning(dcPhilipsHue) << "N-UPNP discovery error:" << status << reply->errorString(); + reply->deleteLater(); + return; + } + processNUpnpResponse(reply->readAll()); + } else if (m_bridgeLightsDiscoveryRequests.keys().contains(reply)) { + Device *device = m_bridgeLightsDiscoveryRequests.take(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcPhilipsHue) << "Bridge light discovery error:" << status << reply->errorString(); + onBridgeError(device); + reply->deleteLater(); + return; + } + processBridgeLightDiscoveryResponse(device, reply->readAll()); + + } else if (m_bridgeSensorsDiscoveryRequests.keys().contains(reply)) { + Device *device = m_bridgeSensorsDiscoveryRequests.take(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcPhilipsHue) << "Bridge sensor discovery error:" << status << reply->errorString(); + onBridgeError(device); + reply->deleteLater(); + return; + } + processBridgeSensorDiscoveryResponse(device, reply->readAll()); + + } else if (m_bridgeSearchDevicesRequests.keys().contains(reply)) { + Device *device = m_bridgeSearchDevicesRequests.take(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcPhilipsHue) << "Bridge search new devices error:" << status << reply->errorString(); + onBridgeError(device); + reply->deleteLater(); + return; + } + + discoverBridgeDevices(m_bridges.key(device)); + + } else if (m_lightRefreshRequests.keys().contains(reply)) { Device *device = m_lightRefreshRequests.take(reply); // check HTTP status code @@ -280,26 +338,54 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) } processLightRefreshResponse(device, reply->readAll()); - } else if (m_bridgeRefreshRequests.keys().contains(reply)) { + } else if (m_lightsRefreshRequests.keys().contains(reply)) { + Device *device = m_lightsRefreshRequests.take(reply); + // check HTTP status code + if (status != 200) { + if (device->stateValue(bridgeReachableStateTypeId).toBool()) { + qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); + onBridgeError(device); + } + reply->deleteLater(); + return; + } + processLightsRefreshResponse(device, reply->readAll()); + + } else if (m_sensorsRefreshRequests.keys().contains(reply)) { + Device *device = m_lightsRefreshRequests.take(reply); + + // check HTTP status code + if (status != 200) { + if (device->stateValue(bridgeReachableStateTypeId).toBool()) { + qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); + onBridgeError(device); + } + reply->deleteLater(); + return; + } + processSensorsRefreshResponse(device, reply->readAll()); + + } else if (m_bridgeRefreshRequests.keys().contains(reply)) { Device *device = m_bridgeRefreshRequests.take(reply); // check HTTP status code if (status != 200) { - qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); - onBridgeError(device); + if (device->stateValue(bridgeReachableStateTypeId).toBool()) { + qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); + onBridgeError(device); + } reply->deleteLater(); return; } processBridgeRefreshResponse(device, reply->readAll()); } else if (m_asyncActions.keys().contains(reply)) { - QPair actionInfo = m_asyncActions.take(reply); // check HTTP status code if (status != 200) { - qCWarning(dcPhilipsHue) << "Refresh Hue Light request error:" << status << reply->errorString(); + qCWarning(dcPhilipsHue) << "Execute Hue Light action request error:" << status << reply->errorString(); onBridgeError(actionInfo.first); emit actionExecutionFinished(actionInfo.second, DeviceManager::DeviceErrorHardwareNotAvailable); reply->deleteLater(); @@ -309,12 +395,12 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) processActionResponse(actionInfo.first, actionInfo.second, data); } else if (m_lightSetNameRequests.keys().contains(reply)) { - Device *device = m_lightSetNameRequests.take(reply); // check HTTP status code if (status != 200) { qCWarning(dcPhilipsHue) << "Set name of Hue Light request error:" << status << reply->errorString(); + onBridgeError(device); reply->deleteLater(); return; } @@ -324,15 +410,10 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) reply->deleteLater(); } -void DevicePluginPhilipsHue::guhTimer() -{ - foreach (Device *device, m_bridges.values()) { - refreshBridge(device); - } -} - DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const Action &action) { + qCDebug(dcPhilipsHue) << "Execute action" << action.actionTypeId() << action.params(); + if (device->deviceClassId() == hueLightDeviceClassId) { HueLight *light = m_lights.key(device); @@ -367,8 +448,41 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, return DeviceManager::DeviceErrorActionTypeNotFound; } + if (device->deviceClassId() == hueWhiteLightDeviceClassId) { + 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() == 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() == 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; + } + return DeviceManager::DeviceErrorActionTypeNotFound; + } + if (device->deviceClassId() == hueBridgeDeviceClassId) { - // TODO: search if a light was added or removed from bridge + if (!device->stateValue(bridgeReachableStateTypeId).toBool()) + return DeviceManager::DeviceErrorHardwareNotAvailable; + + HueBridge *bridge = m_bridges.key(device); + if (action.actionTypeId() == searchNewDevicesActionTypeId) { + searchNewDevices(bridge); + return DeviceManager::DeviceErrorNoError; + } else if (action.actionTypeId() == checkForUpdatesActionTypeId) { + QPair request = bridge->createCheckUpdatesRequest(); + m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id())); + return DeviceManager::DeviceErrorAsync; + } return DeviceManager::DeviceErrorNoError; } return DeviceManager::DeviceErrorDeviceClassNotFound; @@ -382,19 +496,40 @@ void DevicePluginPhilipsHue::lightStateChanged() 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()); + if (device->deviceClassId() == hueLightDeviceClassId) { + 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()); + } else if (device->deviceClassId() == hueWhiteLightDeviceClassId) { + device->setStateValue(hueReachableStateTypeId, light->reachable()); + device->setStateValue(huePowerStateTypeId, light->power()); + device->setStateValue(hueBrightnessStateTypeId, brightnessToPercentage(light->brightness())); + } +} + +void DevicePluginPhilipsHue::onTimeout() +{ + foreach (Device *device, m_bridges.values()) { + HueBridge *bridge = m_bridges.key(device); + if (!device->stateValue(hueReachableStateTypeId).toBool()) { + if (!m_bridgeRefreshRequests.values().contains(device)) + refreshBridge(device); + } else { + refreshBridge(device); + refreshLights(bridge); + refreshSensors(bridge); + } + } } void DevicePluginPhilipsHue::refreshLight(Device *device) { HueLight *light = m_lights.key(device); - QNetworkRequest request(QUrl("http://" + light->hostAddress().toString() + "/api/" + light->apiKey() + "/lights/" + QString::number(light->lightId()))); + QNetworkRequest request(QUrl("http://" + light->hostAddress().toString() + "/api/" + light->apiKey() + "/lights/" + QString::number(light->id()))); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QNetworkReply *reply = networkManagerGet(request); @@ -405,14 +540,57 @@ void DevicePluginPhilipsHue::refreshBridge(Device *device) { HueBridge *bridge = m_bridges.key(device); - QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/lights/")); + QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/config")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QNetworkReply *reply = networkManagerGet(request); m_bridgeRefreshRequests.insert(reply, device); } -void DevicePluginPhilipsHue::setLightName(Device *device, QString name) +void DevicePluginPhilipsHue::refreshLights(HueBridge *bridge) +{ + Device *device = m_bridges.value(bridge); + + QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/lights")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QNetworkReply *reply = networkManagerGet(request); + + m_lightsRefreshRequests.insert(reply, device); +} + +void DevicePluginPhilipsHue::refreshSensors(HueBridge *bridge) +{ + Device *device = m_bridges.value(bridge); + + QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/sensors")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QNetworkReply *reply = networkManagerGet(request); + + m_sensorsRefreshRequests.insert(reply, device); +} + +void DevicePluginPhilipsHue::discoverBridgeDevices(HueBridge *bridge) +{ + Device *device = m_bridges.value(bridge); + qCDebug(dcPhilipsHue) << "Discover bridge devices" << bridge->hostAddress(); + + QPair lightsRequest = bridge->createDiscoverLightsRequest(); + m_bridgeLightsDiscoveryRequests.insert(networkManagerGet(lightsRequest.first), device); + + QPair sensorsRequest = bridge->createSearchSensorsRequest(); + m_bridgeSensorsDiscoveryRequests.insert(networkManagerGet(sensorsRequest.first), device); +} + +void DevicePluginPhilipsHue::searchNewDevices(HueBridge *bridge) +{ + Device *device = m_bridges.value(bridge); + qCDebug(dcPhilipsHue) << "Discover bridge devices" << bridge->hostAddress(); + + QPair request = bridge->createSearchLightsRequest(); + m_bridgeSearchDevicesRequests.insert(networkManagerPost(request.first, request.second), device); +} + +void DevicePluginPhilipsHue::setLightName(Device *device, const QString &name) { HueLight *light = m_lights.key(device); @@ -421,13 +599,166 @@ void DevicePluginPhilipsHue::setLightName(Device *device, QString name) QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); QNetworkRequest request(QUrl("http://" + light->hostAddress().toString() + "/api/" + light->apiKey() + - "/lights/" + QString::number(light->lightId()))); + "/lights/" + QString::number(light->id()))); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QNetworkReply *reply = networkManagerPut(request,jsonDoc.toJson()); m_lightSetNameRequests.insert(reply, device); } +void DevicePluginPhilipsHue::processNUpnpResponse(const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "N-UPNP discovery JSON error in response" << error.errorString(); + return; + } + + QVariantList bridgeList = jsonDoc.toVariant().toList(); + + QList deviceDescriptors; + foreach (const QVariant &bridgeVariant, bridgeList) { + QVariantMap bridgeMap = bridgeVariant.toMap(); + DeviceDescriptor descriptor(hueBridgeDeviceClassId, "Philips Hue Bridge", bridgeMap.value("internalipaddress").toString()); + ParamList params; + params.append(Param("name", "Philips hue")); + params.append(Param("host address", bridgeMap.value("internalipaddress").toString())); + params.append(Param("api key", QString())); + params.append(Param("mac address", QString())); + params.append(Param("id", bridgeMap.value("id").toString().toLower())); + params.append(Param("zigbee channel", -1)); + descriptor.setParams(params); + deviceDescriptors.append(descriptor); + } + qCDebug(dcPhilipsHue) << "N-UPNP discover finished. Found" << deviceDescriptors.count() << "devices."; + emit devicesDiscovered(hueBridgeDeviceClassId, deviceDescriptors); +} + +void DevicePluginPhilipsHue::processBridgeLightDiscoveryResponse(Device *device, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Bridge light discovery json error in response" << error.errorString(); + return; + } + + // check response error + if (data.contains("error")) { + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to discover Hue Bridge lights:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to discover Hue Bridge lights: Invalid error message format"; + } + return; + } + + // create Lights if not already added + QList lightDescriptors; + QList whiteLightDescriptors; + + QVariantMap lightsMap = jsonDoc.toVariant().toMap(); + foreach (QString lightId, lightsMap.keys()) { + QVariantMap lightMap = lightsMap.value(lightId).toMap(); + + QString uuid = lightMap.value("uniqueid").toString(); + QString model = lightMap.value("modelid").toString(); + + if (lightAlreadyAdded(uuid)) + continue; + + // check if this is a white light + if (model == "LWB004" || model == "LWB006" || model == "LWB007") { + DeviceDescriptor descriptor(hueWhiteLightDeviceClassId, "Philips Hue White Light", lightMap.value("name").toString()); + ParamList params; + params.append(Param("name", lightMap.value("name").toString())); + params.append(Param("api key", device->paramValue("api key").toString())); + params.append(Param("bridge", device->id().toString())); + params.append(Param("host address", device->paramValue("host address").toString())); + params.append(Param("model id", model)); + params.append(Param("uuid", uuid)); + params.append(Param("light id", lightId)); + descriptor.setParams(params); + whiteLightDescriptors.append(descriptor); + + qCDebug(dcPhilipsHue) << "Found new white light" << lightMap.value("name").toString() << model; + + } else { + DeviceDescriptor descriptor(hueLightDeviceClassId, "Philips Hue Light", lightMap.value("name").toString()); + ParamList params; + params.append(Param("name", lightMap.value("name").toString())); + params.append(Param("api key", device->paramValue("api key").toString())); + params.append(Param("bridge", device->id().toString())); + params.append(Param("host address", device->paramValue("host address").toString())); + params.append(Param("model id", model)); + params.append(Param("uuid", uuid)); + params.append(Param("light id", lightId)); + descriptor.setParams(params); + lightDescriptors.append(descriptor); + qCDebug(dcPhilipsHue) << "Found new color light" << lightMap.value("name").toString() << model; + } + } + emit autoDevicesAppeared(hueLightDeviceClassId, lightDescriptors); + emit autoDevicesAppeared(hueWhiteLightDeviceClassId, whiteLightDescriptors); +} + +void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Bridge sensor discovery json error in response" << error.errorString(); + return; + } + + // check response error + if (data.contains("error")) { + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to discover Hue Bridge sensors:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to discover Hue Bridge sensors: Invalid error message format"; + } + return; + } + + // create Lights if not already added + QList sensorDescriptors; + + QVariantMap sensorsMap = jsonDoc.toVariant().toMap(); + foreach (QString sensorId, sensorsMap.keys()) { + QVariantMap sensorMap = sensorsMap.value(sensorId).toMap(); + + QString uuid = sensorMap.value("uniqueid").toString(); + QString model = sensorMap.value("modelid").toString(); + + if (sensorAlreadyAdded(uuid)) + continue; + + // check if this is a white light + if (model == "RWL021") { + DeviceDescriptor descriptor(hueRemoteDeviceClassId, "Philips Hue Remote", sensorMap.value("name").toString()); + ParamList params; + params.append(Param("name", sensorMap.value("name").toString())); + params.append(Param("api key", device->paramValue("api key").toString())); + params.append(Param("bridge", device->id().toString())); + params.append(Param("host address", device->paramValue("host address").toString())); + params.append(Param("model id", model)); + params.append(Param("uuid", uuid)); + params.append(Param("sensor id", sensorId)); + descriptor.setParams(params); + sensorDescriptors.append(descriptor); + qCDebug(dcPhilipsHue) << "Found new remote" << sensorMap.value("name").toString() << model; + } + } +} + void DevicePluginPhilipsHue::processLightRefreshResponse(Device *device, const QByteArray &data) { QJsonParseError error; @@ -441,12 +772,16 @@ void DevicePluginPhilipsHue::processLightRefreshResponse(Device *device, const Q // check response error if (data.contains("error")) { - qCWarning(dcPhilipsHue) << "Failed to refresh Hue Light:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Light:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Light: Invalid error message format"; + } return; } - HueLight *hueLight = m_lights.key(device); - hueLight->updateStates(jsonDoc.toVariant().toMap().value("state").toMap()); + HueRemote *remote = m_remotes.key(device); + remote->updateStates(jsonDoc.toVariant().toMap().value("state").toMap()); } void DevicePluginPhilipsHue::processBridgeRefreshResponse(Device *device, const QByteArray &data) @@ -460,27 +795,94 @@ void DevicePluginPhilipsHue::processBridgeRefreshResponse(Device *device, const return; } - // check response error - if (data.contains("error")) { + if (!jsonDoc.toVariant().toList().isEmpty()) { qCWarning(dcPhilipsHue) << "Failed to refresh Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + onBridgeError(device); return; } + QVariantMap configMap = jsonDoc.toVariant().toMap(); + // mark bridge as reachable device->setStateValue(bridgeReachableStateTypeId, true); + device->setStateValue(apiVersionStateTypeId, configMap.value("apiversion").toString()); + device->setStateValue(softwareVersionStateTypeId, configMap.value("swversion").toString()); + int updateStatus = configMap.value("swupdate").toMap().value("updatestate").toInt(); + switch (updateStatus) { + case 0: + device->setStateValue(updateStatusStateTypeId, "Up to date"); + break; + case 1: + device->setStateValue(updateStatusStateTypeId, "Downloading updates"); + break; + case 2: + device->setStateValue(updateStatusStateTypeId, "Updates ready to install"); + break; + case 3: + device->setStateValue(updateStatusStateTypeId, "Installing updates"); + break; + default: + break; + } +} + +void DevicePluginPhilipsHue::processLightsRefreshResponse(Device *device, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); + return; + } + + // check response error + if (data.contains("error")) { + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Lights:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Lights: Invalid error message format"; + } + return; + } + + // Update light states 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()) { + if (light->id() == lightId.toInt() && light->bridgeId() == device->id()) { light->updateStates(lightMap.value("state").toMap()); } } } } +void DevicePluginPhilipsHue::processSensorsRefreshResponse(Device *device, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); + return; + } + + // check response error + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Sensors:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + return; + } + + //qCDebug(dcPhilipsHue) << jsonDoc.toJson(); + + Q_UNUSED(device) +} + void DevicePluginPhilipsHue::processSetNameResponse(Device *device, const QByteArray &data) { QJsonParseError error; @@ -495,14 +897,18 @@ void DevicePluginPhilipsHue::processSetNameResponse(Device *device, const QByteA // check response error if (data.contains("error")) { - qCWarning(dcPhilipsHue) << "Failed to set name of Hue:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to set name of Hue:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to set name of Hue: Invalid error message format"; + } emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure); return; } emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess); - if (device->deviceClassId() == hueLightDeviceClassId) + if (device->deviceClassId() == hueLightDeviceClassId || device->deviceClassId() == hueWhiteLightDeviceClassId) refreshLight(device); } @@ -522,7 +928,11 @@ void DevicePluginPhilipsHue::processPairingResponse(PairingInfo *pairingInfo, co // check response error if (data.contains("error")) { - qCWarning(dcPhilipsHue) << "Failed to pair Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to pair Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to pair Hue Bridge: Invalid error message format"; + } emit pairingFinished(pairingInfo->pairingTransactionId(), DeviceManager::DeviceSetupStatusFailure); pairingInfo->deleteLater(); return; @@ -533,14 +943,14 @@ void DevicePluginPhilipsHue::processPairingResponse(PairingInfo *pairingInfo, co qCDebug(dcPhilipsHue) << "Got api key from bridge:" << pairingInfo->apiKey(); if (pairingInfo->apiKey().isEmpty()) { - qCWarning(dcPhilipsHue) << "Failed to pair Hue Bridge: did not get any username from the bridge"; + qCWarning(dcPhilipsHue) << "Failed to pair Hue Bridge: did not get any key from the bridge"; emit pairingFinished(pairingInfo->pairingTransactionId(), DeviceManager::DeviceSetupStatusFailure); pairingInfo->deleteLater(); return; } - // Paired successfully, check bridge/light information - QNetworkRequest request(QUrl("http://" + pairingInfo->host().toString() + "/api/" + pairingInfo->apiKey() + "/")); + // Paired successfully, check bridge information + QNetworkRequest request(QUrl("http://" + pairingInfo->host().toString() + "/api/" + pairingInfo->apiKey() + "/config")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QNetworkReply *reply = networkManagerGet(request); @@ -552,8 +962,6 @@ void DevicePluginPhilipsHue::processInformationResponse(PairingInfo *pairingInfo QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - //qCDebug(dcPhilipsHue) << "Process information response" << pairingInfo->host().toString() << pairingInfo->apiKey(); - // check JSON error if (error.error != QJsonParseError::NoError) { qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); @@ -573,35 +981,18 @@ void DevicePluginPhilipsHue::processInformationResponse(PairingInfo *pairingInfo } // create Bridge - HueBridge *bridge = new HueBridge(pairingInfo->apiKey(), pairingInfo->host(), this); - - bridge->setApiVersion(response.value("config").toMap().value("apiversion").toString()); - bridge->setSoftwareVersion(response.value("config").toMap().value("swversion").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()); + HueBridge *bridge = new HueBridge(this); + bridge->setId(response.value("bridgeid").toString()); + bridge->setApiKey(pairingInfo->apiKey()); + bridge->setHostAddress(pairingInfo->host()); + bridge->setApiVersion(response.value("apiversion").toString()); + bridge->setSoftwareVersion(response.value("swversion").toString()); + bridge->setMacAddress(response.value("mac").toString()); + bridge->setName(response.value("name").toString()); + bridge->setZigbeeChannel(response.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->apiKey(), - lightMap.value("modelid").toString(), - DeviceId(), - this); - - hueLight->updateStates(lightMap.value("state").toMap()); - - bridge->addLight(hueLight); - m_unconfiguredLights.append(hueLight); - - connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); - } emit pairingFinished(pairingInfo->pairingTransactionId(), DeviceManager::DeviceSetupStatusSuccess); pairingInfo->deleteLater(); } @@ -620,12 +1011,18 @@ void DevicePluginPhilipsHue::processActionResponse(Device *device, const ActionI // check response error if (data.contains("error")) { - qCWarning(dcPhilipsHue) << "Failed to execute Hue action:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to execute Hue action:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to execute Hue action: Invalid error message format"; + } emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable); return; } - m_lights.key(device)->processActionResponse(jsonDoc.toVariant().toList()); + if (device->deviceClassId() != hueBridgeDeviceClassId) + m_lights.key(device)->processActionResponse(jsonDoc.toVariant().toList()); + emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); } @@ -636,33 +1033,55 @@ void DevicePluginPhilipsHue::onBridgeError(Device *device) device->setStateValue(bridgeReachableStateTypeId, false); foreach (HueLight *light, m_lights.keys()) { if (light->bridgeId() == device->id()) { + light->setReachable(false); device->setStateValue(hueReachableStateTypeId, false); } } - } else if (device->deviceClassId() == hueLightDeviceClassId) { - DeviceId bridgeId = DeviceId(device->paramValue("bridge").toString()); - // mark lamps as unreachable - foreach (HueLight *light, m_lights.keys()) { - if (light->bridgeId() == bridgeId) { - device->setStateValue(hueReachableStateTypeId, false); - } - } - // mark bridge as unreachable - foreach (Device *d, m_bridges.values()) { - if (d->id() == bridgeId) { - d->setStateValue(bridgeReachableStateTypeId, false); - return; - } - } } } +bool DevicePluginPhilipsHue::bridgeAlreadyAdded(const QString &id) +{ + foreach (Device *device, myDevices()) { + if (device->deviceClassId() == hueBridgeDeviceClassId) { + if (device->paramValue("id").toString() == id) { + return true; + } + } + } + return false; +} + +bool DevicePluginPhilipsHue::lightAlreadyAdded(const QString &uuid) +{ + foreach (Device *device, myDevices()) { + if (device->deviceClassId() == hueLightDeviceClassId || device->deviceClassId() == hueWhiteLightDeviceClassId) { + if (device->paramValue("uuid").toString() == uuid) { + return true; + } + } + } + return false; +} + +bool DevicePluginPhilipsHue::sensorAlreadyAdded(const QString &uuid) +{ + foreach (Device *device, myDevices()) { + if (device->deviceClassId() == hueRemoteDeviceClassId) { + if (device->paramValue("uuid").toString() == uuid) { + return true; + } + } + } + return false; +} + int DevicePluginPhilipsHue::brightnessToPercentage(int brightness) { - return (int)(((100.0 * brightness) / 255.0) + 0.5); + return qRound((100.0 * brightness) / 255.0); } int DevicePluginPhilipsHue::percentageToBrightness(int percentage) { - return (int)(((255.0 * percentage) / 100.0) + 0.5); + return qRound((255.0 * percentage) / 100.0); } diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.h b/plugins/deviceplugins/philipshue/devicepluginphilipshue.h index 712f0e27..ed8d5b65 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.h +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.h @@ -25,6 +25,7 @@ #include "plugin/deviceplugin.h" #include "huebridge.h" #include "huelight.h" +#include "hueremote.h" #include "pairinginfo.h" class QNetworkReply; @@ -40,45 +41,67 @@ public: explicit DevicePluginPhilipsHue(); DeviceManager::HardwareResources requiredHardware() const 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; + void upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) override; DeviceManager::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret) override; void networkManagerReplyReady(QNetworkReply *reply) override; - void guhTimer() override; - public slots: DeviceManager::DeviceError executeAction(Device *device, const Action &action); private slots: void lightStateChanged(); + void onTimeout(); private: + QTimer *m_timer; + QHash m_pairingRequests; QHash m_informationRequests; QList m_unconfiguredBridges; QList m_unconfiguredLights; + QList m_discoveryRequests; QHash m_lightRefreshRequests; QHash m_lightSetNameRequests; QHash m_bridgeRefreshRequests; + QHash m_lightsRefreshRequests; + QHash m_sensorsRefreshRequests; + QHash m_bridgeLightsDiscoveryRequests; + QHash m_bridgeSensorsDiscoveryRequests; + QHash m_bridgeSearchDevicesRequests; + QHash > m_asyncActions; - QHash m_bridges; - QHash m_lights; + QHash m_bridges; + QHash m_lights; + QHash m_remotes; void refreshLight(Device *device); void refreshBridge(Device *device); - void setLightName(Device *device, QString name); + void refreshLights(HueBridge *bridge); + void refreshSensors(HueBridge *bridge); + void discoverBridgeDevices(HueBridge *bridge); + void searchNewDevices(HueBridge *bridge); + + void setLightName(Device *device, const QString &name); + + void processNUpnpResponse(const QByteArray &data); + void processBridgeLightDiscoveryResponse(Device *device, const QByteArray &data); + void processBridgeSensorDiscoveryResponse(Device *device, const QByteArray &data); void processLightRefreshResponse(Device *device, const QByteArray &data); void processBridgeRefreshResponse(Device *device, const QByteArray &data); + void processLightsRefreshResponse(Device *device, const QByteArray &data); + void processSensorsRefreshResponse(Device *device, const QByteArray &data); void processSetNameResponse(Device *device, const QByteArray &data); void processPairingResponse(PairingInfo *pairingInfo, const QByteArray &data); void processInformationResponse(PairingInfo *pairingInfo, const QByteArray &data); @@ -86,6 +109,10 @@ private: void onBridgeError(Device *device); + bool bridgeAlreadyAdded(const QString &id); + bool lightAlreadyAdded(const QString &uuid); + bool sensorAlreadyAdded(const QString &uuid); + int brightnessToPercentage(int brightness); int percentageToBrightness(int percentage); }; diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json index e25149c8..80253ca8 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json @@ -11,7 +11,7 @@ { "deviceClassId": "642aa4c7-19aa-45ed-ba06-aa1ae6c9edf7", "idName": "hueBridge", - "name": "Hue Bridge", + "name": "Hue gateway", "createMethods": ["discovery"], "setupMethod": "pushButton", "pairingInfo": "Please press the button on the Hue Bridge within 30 seconds before you continue", @@ -40,7 +40,7 @@ "readOnly": true }, { - "name": "api version", + "name": "id", "type" : "QString", "readOnly": true }, @@ -60,7 +60,47 @@ "id": "15794d26-fde8-4a61-8f83-d7830534975f", "idName": "bridgeReachable", "name": "reachable", + "defaultValue": false, "type": "bool" + }, + { + "id": "7a230e89-c4ce-4276-90e0-6a9ddb890603", + "idName": "apiVersion", + "name": "api version", + "defaultValue": "-", + "type": "QString" + }, + { + "id": "4c707b18-6604-4e6d-b6bc-4e27769c2adc", + "idName": "softwareVersion", + "name": "software version", + "defaultValue": "-", + "type": "QString" + }, + { + "id": "16a126f3-0cef-4931-bb2b-9e1b49bec7fc", + "idName": "updateStatus", + "name": "update status", + "type": "QString", + "defaultValue": "Up to date", + "possibleValues": [ + "Up to date", + "Downloading updates", + "Updates ready to install", + "Installing updates" + ] + } + ], + "actionTypes": [ + { + "id": "cca3f171-6318-44e7-a2ac-d841857c1c24", + "idName": "searchNewDevices", + "name": "search devices" + }, + { + "id": "07a85e91-d064-4bce-b017-13fd0c320c0b", + "idName": "checkForUpdates", + "name": "check updates" } ] }, @@ -85,6 +125,16 @@ "type" : "QString", "readOnly": true }, + { + "name": "type", + "type" : "QString", + "readOnly": true + }, + { + "name": "uuid", + "type" : "QString", + "readOnly": true + }, { "name": "api key", "type" : "QString", @@ -108,6 +158,7 @@ "id": "19bb8d10-1b28-4ba3-99b7-a634138dcfde", "idName": "hueReachable", "name": "reachable", + "defaultValue": false, "type": "bool" }, { @@ -180,6 +231,193 @@ ] } ] + }, + { + "deviceClassId": "4fa568ef-7a3a-422b-b0c0-206d37cb4eed", + "idName": "hueWhiteLight", + "name": "Hue White Light", + "createMethods": ["auto"], + "paramTypes": [ + { + "name": "name", + "type" : "QString", + "inputType": "TextLine" + }, + { + "name": "bridge", + "type" : "QString", + "readOnly": true + }, + { + "name": "model id", + "type" : "QString", + "readOnly": true + }, + { + "name": "type", + "type" : "QString", + "readOnly": true + }, + { + "name": "uuid", + "type" : "QString", + "readOnly": true + }, + { + "name": "api key", + "type" : "QString", + "inputType": "TextLine", + "readOnly": true + }, + { + "name": "host address", + "type" : "QString", + "inputType": "IPv4Address", + "readOnly": true + }, + { + "name": "light id", + "type" : "int", + "readOnly": true + } + ], + "stateTypes": [ + { + "id": "19bb8d10-1b28-4ba3-99b7-a634138dcfde", + "idName": "hueReachable", + "name": "reachable", + "defaultValue": false, + "type": "bool" + }, + { + "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", + "defaultValue": 0, + "minValue": 0, + "maxValue": 100, + "writable": true + + } + ], + "actionTypes": [ + { + "id": "d25dcfbc-d28c-4905-80e3-300ffb1248f5", + "idName": "hueAlert", + "name": "flash", + "paramTypes": [ + { + "name": "alert", + "type": "QString", + "allowedValues": [ + "flash once", + "flash 30 seconds" + ] + } + ] + } + ] + }, + { + "deviceClassId": "bb482d39-67ef-46dc-88e9-7b181d642b28", + "idName": "hueRemote", + "name": "Hue Remote", + "createMethods": ["auto"], + "paramTypes": [ + { + "name": "name", + "type" : "QString", + "inputType": "TextLine" + }, + { + "name": "bridge", + "type" : "QString", + "readOnly": true + }, + { + "name": "model id", + "type" : "QString", + "readOnly": true + }, + { + "name": "type", + "type" : "QString", + "readOnly": true + }, + { + "name": "uuid", + "type" : "QString", + "readOnly": true + }, + { + "name": "api key", + "type" : "QString", + "inputType": "TextLine", + "readOnly": true + }, + { + "name": "host address", + "type" : "QString", + "inputType": "IPv4Address", + "readOnly": true + }, + { + "name": "sensor id", + "type" : "int", + "readOnly": true + } + ], + "stateTypes": [ + { + "id": "19bb8d10-1b28-4ba3-99b7-a634138dcfde", + "idName": "hueReachable", + "name": "reachable", + "defaultValue": false, + "type": "bool" + }, + { + "id": "683e493a-9796-4d5e-b0e3-61cb178d5819", + "idName": "battery", + "name": "battery", + "type": "int", + "unit": "Percentage", + "defaultValue": 0, + "minValue": 0, + "maxValue": 100 + } + ], + "eventTypes": [ + { + "id": "de769db0-4c31-46cf-9760-dbc6f9209c26", + "idName": "onButton", + "name": "on" + }, + { + "id": "8e3d6a62-6a19-4e9a-a25b-e1da2e56ede9", + "idName": "brightnessUpButton", + "name": "brightness up" + }, + { + "id": "efd8b972-9a37-43f2-b9bc-f9dfe144a96d", + "idName": "brightnessDownButton", + "name": "brightness down" + }, + { + "id": "7c2a58f1-137c-4bf3-8f9e-453dff020487", + "idName": "offButton", + "name": "off" + } + ] } ] } diff --git a/plugins/deviceplugins/philipshue/huebridge.cpp b/plugins/deviceplugins/philipshue/huebridge.cpp index b52c3df2..7e1f50ef 100644 --- a/plugins/deviceplugins/philipshue/huebridge.cpp +++ b/plugins/deviceplugins/philipshue/huebridge.cpp @@ -20,14 +20,8 @@ #include "huebridge.h" -HueBridge::HueBridge(QString apiKey, QHostAddress hostAddress, QObject *parent) : +HueBridge::HueBridge(QObject *parent) : QObject(parent), - m_apiKey(apiKey), - m_hostAddress(hostAddress), - m_name(QString()), - m_macAddress(QString()), - m_apiVersion(QString()), - m_softwareVersion(QString()), m_zigbeeChannel(-1) { @@ -43,6 +37,16 @@ void HueBridge::setName(const QString &name) m_name = name; } +QString HueBridge::id() const +{ + return m_id; +} + +void HueBridge::setId(const QString &id) +{ + m_id = id; +} + QString HueBridge::apiKey() const { return m_apiKey; @@ -112,3 +116,41 @@ void HueBridge::addLight(HueLight *light) { m_lights.append(light); } + +QPair HueBridge::createDiscoverLightsRequest() +{ + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + "/lights/")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + return QPair(request, QByteArray()); +} + +QPair HueBridge::createSearchLightsRequest() +{ + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + "/lights/")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + return QPair(request, QByteArray()); +} + +QPair HueBridge::createSearchSensorsRequest() +{ + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + "/sensors/")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + return QPair(request, QByteArray()); +} + +QPair HueBridge::createCheckUpdatesRequest() +{ + QVariantMap updateMap; + updateMap.insert("checkforupdate", true); + + QVariantMap requestMap; + requestMap.insert("swupdate", updateMap); + requestMap.insert("portalservices", true); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); + + QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + "/config")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + return QPair(request, jsonDoc.toJson()); +} diff --git a/plugins/deviceplugins/philipshue/huebridge.h b/plugins/deviceplugins/philipshue/huebridge.h index 21cd66cf..5f338a34 100644 --- a/plugins/deviceplugins/philipshue/huebridge.h +++ b/plugins/deviceplugins/philipshue/huebridge.h @@ -30,11 +30,14 @@ class HueBridge : public QObject { Q_OBJECT public: - explicit HueBridge(QString apiKey, QHostAddress hostAddress, QObject *parent = 0); + explicit HueBridge(QObject *parent = 0); QString name() const; void setName(const QString &name); + QString id() const; + void setId(const QString &id); + QString apiKey() const; void setApiKey(const QString &apiKey); @@ -56,7 +59,13 @@ public: QList lights() const; void addLight(HueLight *light); + QPair createDiscoverLightsRequest(); + QPair createSearchLightsRequest(); + QPair createSearchSensorsRequest(); + QPair createCheckUpdatesRequest(); + private: + QString m_id; QString m_apiKey; QHostAddress m_hostAddress; QString m_name; diff --git a/plugins/deviceplugins/philipshue/huedevice.cpp b/plugins/deviceplugins/philipshue/huedevice.cpp new file mode 100644 index 00000000..3a7b1762 --- /dev/null +++ b/plugins/deviceplugins/philipshue/huedevice.cpp @@ -0,0 +1,108 @@ +#include "huedevice.h" + +HueDevice::HueDevice(QObject *parent) : + QObject(parent) +{ + +} + +int HueDevice::id() const +{ + return m_id; +} + +void HueDevice::setId(const int &id) +{ + m_id = id; +} + +QString HueDevice::name() const +{ + return m_name; +} + +void HueDevice::setName(const QString &name) +{ + m_name = name; +} + +DeviceId HueDevice::bridgeId() const +{ + return m_bridgeId; +} + +void HueDevice::setBridgeId(const DeviceId &bridgeId) +{ + m_bridgeId = bridgeId; +} + +QHostAddress HueDevice::hostAddress() const +{ + return m_hostAddress; +} + +void HueDevice::setHostAddress(const QHostAddress hostAddress) +{ + m_hostAddress = hostAddress; +} + +QString HueDevice::uuid() +{ + return m_uuid; +} + +void HueDevice::setUuid(const QString &uuid) +{ + m_uuid = uuid; +} + +QString HueDevice::apiKey() const +{ + return m_apiKey; +} + +void HueDevice::setApiKey(const QString &apiKey) +{ + m_apiKey = apiKey; +} + +QString HueDevice::modelId() const +{ + return m_modelId; +} + +void HueDevice::setModelId(const QString &modelId) +{ + m_modelId = modelId; +} + +QString HueDevice::type() const +{ + return m_type; +} + +void HueDevice::setType(const QString &type) +{ + m_type = type; +} + +QString HueDevice::softwareVersion() const +{ + return m_softwareVersion; +} + +void HueDevice::setSoftwareVersion(const QString &softwareVersion) +{ + m_softwareVersion = softwareVersion; +} + +bool HueDevice::reachable() const +{ + return m_reachable; +} + +void HueDevice::setReachable(const bool &reachable) +{ + m_reachable = reachable; +} + diff --git a/plugins/deviceplugins/philipshue/huedevice.h b/plugins/deviceplugins/philipshue/huedevice.h new file mode 100644 index 00000000..6aba33c5 --- /dev/null +++ b/plugins/deviceplugins/philipshue/huedevice.h @@ -0,0 +1,62 @@ +#ifndef HUEDEVICE_H +#define HUEDEVICE_H + +#include +#include +#include +#include +#include + +#include "typeutils.h" + +class HueDevice : public QObject +{ + Q_OBJECT +public: + explicit HueDevice(QObject *parent = 0); + + int id() const; + void setId(const int &id); + + QString name() const; + void setName(const QString &name); + + DeviceId bridgeId() const; + void setBridgeId(const DeviceId &bridgeId); + + QHostAddress hostAddress() const; + void setHostAddress(const QHostAddress hostAddress); + + QString uuid(); + void setUuid(const QString &uuid); + + QString apiKey() const; + void setApiKey(const QString &apiKey); + + QString modelId() const; + void setModelId(const QString &modelId); + + QString type() const; + void setType(const QString &type); + + QString softwareVersion() const; + void setSoftwareVersion(const QString &softwareVersion); + + bool reachable() const; + void setReachable(const bool &reachable); + +private: + int m_id; + QString m_name; + QHostAddress m_hostAddress; + QString m_apiKey; + QString m_modelId; + QString m_uuid; + DeviceId m_bridgeId; + QString m_type; + QString m_softwareVersion; + + bool m_reachable; +}; + +#endif // HUEDEVICE_H diff --git a/plugins/deviceplugins/philipshue/huelight.cpp b/plugins/deviceplugins/philipshue/huelight.cpp index 4f27dd9b..e1edcee8 100644 --- a/plugins/deviceplugins/philipshue/huelight.cpp +++ b/plugins/deviceplugins/philipshue/huelight.cpp @@ -22,97 +22,11 @@ #include "huelight.h" #include "extern-plugininfo.h" -HueLight::HueLight(const int &lightId, const QHostAddress &hostAddress, const QString &name, const QString &apiKey, const QString &modelId, const DeviceId &bridgeId, QObject *parent) : - QObject(parent), - m_lightId(lightId), - m_hostAddress(hostAddress), - m_name(name), - m_apiKey(apiKey), - m_modelId(modelId), - m_bridgeId(bridgeId) +HueLight::HueLight(QObject *parent) : + HueDevice(parent) { } -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::apiKey() const -{ - return m_apiKey; -} - -void HueLight::setApiKey(const QString &apiKey) -{ - m_apiKey = apiKey; -} - -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; @@ -123,16 +37,6 @@ 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; @@ -244,7 +148,8 @@ void HueLight::updateStates(const QVariantMap &statesMap) 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())); + if (!statesMap.value("xy").toList().isEmpty()) + setXy(QPointF(statesMap.value("xy").toList().first().toFloat(), statesMap.value("xy").toList().last().toFloat())); emit stateChanged(); } @@ -255,38 +160,38 @@ void HueLight::processActionResponse(const QVariantList &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(id()) + "/state/on")) { + m_power = successMap.value("/lights/" + QString::number(id()) + "/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(); + if (successMap.contains("/lights/" + QString::number(id()) + "/state/hue")) { + m_hue = successMap.value("/lights/" + QString::number(id()) + "/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(id()) + "/state/bri")) { + m_brightness = successMap.value("/lights/" + QString::number(id()) + "/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(); + if (successMap.contains("/lights/" + QString::number(id()) + "/state/sat")) { + m_sat = successMap.value("/lights/" + QString::number(id()) + "/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(); + if (successMap.contains("/lights/" + QString::number(id()) + "/state/xy")) { + m_xy = successMap.value("/lights/" + QString::number(id()) + "/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(); + if (successMap.contains("/lights/" + QString::number(id()) + "/state/ct")) { + m_ct = successMap.value("/lights/" + QString::number(id()) + "/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 (successMap.contains("/lights/" + QString::number(id()) + "/state/effect")) { + QString effect = successMap.value("/lights/" + QString::number(id()) + "/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")) { - m_alert = successMap.value("/lights/" + QString::number(m_lightId) + "/state/alert").toString(); + if (successMap.contains("/lights/" + QString::number(id()) + "/state/alert")) { + m_alert = successMap.value("/lights/" + QString::number(id()) + "/state/alert").toString(); } } @@ -302,8 +207,8 @@ QPair HueLight::createSetPowerRequest(const bool &p QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + - "/lights/" + QString::number(lightId()) + "/state")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + "/lights/" + QString::number(id()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); return QPair(request, jsonDoc.toJson()); } @@ -317,8 +222,8 @@ QPair HueLight::createSetColorRequest(const QColor QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + - "/lights/" + QString::number(lightId()) + "/state")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + "/lights/" + QString::number(id()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); return QPair(request, jsonDoc.toJson()); } @@ -335,8 +240,8 @@ QPair HueLight::createSetBrightnessRequest(const in QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + - "/lights/" + QString::number(lightId()) + "/state")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + "/lights/" + QString::number(id()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); return QPair(request, jsonDoc.toJson()); } @@ -352,8 +257,8 @@ QPair HueLight::createSetEffectRequest(const QStrin QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + - "/lights/" + QString::number(lightId()) + "/state")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + "/lights/" + QString::number(id()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); return QPair(request, jsonDoc.toJson()); } @@ -366,8 +271,8 @@ QPair HueLight::createSetTemperatureRequest(const i QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + - "/lights/" + QString::number(lightId()) + "/state")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + "/lights/" + QString::number(id()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); return QPair(request, jsonDoc.toJson()); } @@ -382,7 +287,7 @@ QPair HueLight::createFlashRequest(const QString &a QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap); QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + - "/lights/" + QString::number(lightId()) + "/state")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + "/lights/" + QString::number(id()) + "/state")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); return QPair(request, jsonDoc.toJson()); } diff --git a/plugins/deviceplugins/philipshue/huelight.h b/plugins/deviceplugins/philipshue/huelight.h index e82b09c4..55daa51b 100644 --- a/plugins/deviceplugins/philipshue/huelight.h +++ b/plugins/deviceplugins/philipshue/huelight.h @@ -31,9 +31,9 @@ #include #include "typeutils.h" -#include "types/action.h" +#include "huedevice.h" -class HueLight : public QObject +class HueLight : public HueDevice { Q_OBJECT public: @@ -44,38 +44,11 @@ public: ColorModeCT }; - explicit HueLight(const int &lightId, const QHostAddress &hostAddress, const QString &name, const QString &apiKey, const QString &modelId, const DeviceId &bridgeId, QObject *parent = 0); - - int lightId() const; - void setLightId(const int &lightId); - - 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 apiKey() const; - void setApiKey(const QString &apiKey); - - QString modelId() const; - void setModelId(const QString &modelId); - - QString type() const; - void setType(const QString &type); - - QString softwareVersion() const; - void setSoftwareVersion(const QString &softwareVersion); + explicit HueLight(QObject *parent = 0); bool power() const; void setPower(const bool &power); - bool reachable() const; - void setReachable(const bool &reachable); - quint8 brightness() const; void setBrigtness(const quint8 brightness); @@ -115,17 +88,7 @@ public: QPair createFlashRequest(const QString &alert); private: - int m_lightId; - QHostAddress m_hostAddress; - QString m_name; - QString m_apiKey; - QString m_modelId; - DeviceId m_bridgeId; - QString m_type; - QString m_softwareVersion; - bool m_power; - bool m_reachable; quint8 m_brightness; quint16 m_hue; diff --git a/plugins/deviceplugins/philipshue/hueremote.cpp b/plugins/deviceplugins/philipshue/hueremote.cpp new file mode 100644 index 00000000..06145d80 --- /dev/null +++ b/plugins/deviceplugins/philipshue/hueremote.cpp @@ -0,0 +1,43 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 "hueremote.h" +#include "extern-plugininfo.h" + +HueRemote::HueRemote(QObject *parent) : + HueDevice(parent) +{ +} + +int HueRemote::battery() const +{ + return m_battery; +} + +void HueRemote::setBattery(const int &battery) +{ + m_battery = battery; +} + +void HueRemote::updateStates(const QVariantMap &statesMap) +{ + qCDebug(dcPhilipsHue) << statesMap; +} + diff --git a/plugins/deviceplugins/philipshue/hueremote.h b/plugins/deviceplugins/philipshue/hueremote.h new file mode 100644 index 00000000..4b2fb4b8 --- /dev/null +++ b/plugins/deviceplugins/philipshue/hueremote.h @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef HUEREMOTE_H +#define HUEREMOTE_H + +#include +#include +#include +#include +#include + +#include "typeutils.h" +#include "huedevice.h" + +class HueRemote : public HueDevice +{ + Q_OBJECT +public: + explicit HueRemote(QObject *parent = 0); + + int battery() const; + void setBattery(const int &battery); + + void updateStates(const QVariantMap &statesMap); + +private: + int m_battery; + +signals: + void onPressed(); + void brightnessUpPressed(); + void brightnessDownPressed(); + void offPressed(); + +public slots: +}; + +#endif // HUEREMOTE_H diff --git a/plugins/deviceplugins/philipshue/philipshue.pro b/plugins/deviceplugins/philipshue/philipshue.pro index ad72daff..44265c2d 100644 --- a/plugins/deviceplugins/philipshue/philipshue.pro +++ b/plugins/deviceplugins/philipshue/philipshue.pro @@ -10,7 +10,9 @@ SOURCES += \ #light.cpp \ huebridge.cpp \ huelight.cpp \ - pairinginfo.cpp + pairinginfo.cpp \ + hueremote.cpp \ + huedevice.cpp HEADERS += \ devicepluginphilipshue.h \ @@ -19,7 +21,9 @@ HEADERS += \ #lightinterface.h \ huebridge.h \ huelight.h \ - pairinginfo.h + pairinginfo.h \ + hueremote.h \ + huedevice.h diff --git a/server/jsonrpc/devicehandler.cpp b/server/jsonrpc/devicehandler.cpp index 5fc2af15..1f4143ce 100644 --- a/server/jsonrpc/devicehandler.cpp +++ b/server/jsonrpc/devicehandler.cpp @@ -607,7 +607,10 @@ void DeviceHandler::devicesDiscovered(const DeviceClassId &deviceClassId, const return; // We didn't start this discovery... Ignore it. } - JsonReply *reply = m_discoverRequests.take(deviceClassId); + JsonReply *reply = 0; + reply = m_discoverRequests.take(deviceClassId); + if (!reply) + return; QVariantMap returns; returns.insert("deviceDescriptors", JsonTypes::packDeviceDescriptors(deviceDescriptors));