rewrite hue plugin

This commit is contained in:
Simon Stürz 2015-12-17 22:38:48 +01:00 committed by Michael Zanetti
parent 15e8b4fbc8
commit 1dae30eb80
14 changed files with 1205 additions and 326 deletions

View File

@ -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. */

View File

@ -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 &params)
@ -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<DeviceDescriptor> 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<UpnpDeviceDescriptor> &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<DeviceDescriptor> 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<Device *, ActionId> 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<QNetworkRequest, QByteArray> request = light->createSetPowerRequest(action.param("power").value().toBool());
m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair<Device *, ActionId>(device, action.id()));
return DeviceManager::DeviceErrorAsync;
} else if (action.actionTypeId() == hueBrightnessActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = light->createSetBrightnessRequest(percentageToBrightness(action.param("brightness").value().toInt()));
m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair<Device *, ActionId>(device, action.id()));
return DeviceManager::DeviceErrorAsync;
} else if (action.actionTypeId() == hueAlertActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = light->createFlashRequest(action.param("alert").value().toString());
m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair<Device *, ActionId>(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<QNetworkRequest, QByteArray> request = bridge->createCheckUpdatesRequest();
m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair<Device *, ActionId>(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<QNetworkRequest, QByteArray> lightsRequest = bridge->createDiscoverLightsRequest();
m_bridgeLightsDiscoveryRequests.insert(networkManagerGet(lightsRequest.first), device);
QPair<QNetworkRequest, QByteArray> 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<QNetworkRequest, QByteArray> 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<DeviceDescriptor> 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<DeviceDescriptor> lightDescriptors;
QList<DeviceDescriptor> 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<DeviceDescriptor> 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);
}

View File

@ -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 &params) override;
void deviceRemoved(Device *device) override;
void upnpDiscoveryFinished(const QList<UpnpDeviceDescriptor> &upnpDeviceDescriptorList) override;
void upnpDiscoveryFinished(const QList<UpnpDeviceDescriptor> &upnpDeviceDescriptorList) override;
DeviceManager::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList &params, 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<QNetworkReply *, PairingInfo *> m_pairingRequests;
QHash<QNetworkReply *, PairingInfo *> m_informationRequests;
QList<HueBridge *> m_unconfiguredBridges;
QList<HueLight *> m_unconfiguredLights;
QList<QNetworkReply *> m_discoveryRequests;
QHash<QNetworkReply *, Device *> m_lightRefreshRequests;
QHash<QNetworkReply *, Device *> m_lightSetNameRequests;
QHash<QNetworkReply *, Device *> m_bridgeRefreshRequests;
QHash<QNetworkReply *, Device *> m_lightsRefreshRequests;
QHash<QNetworkReply *, Device *> m_sensorsRefreshRequests;
QHash<QNetworkReply *, Device *> m_bridgeLightsDiscoveryRequests;
QHash<QNetworkReply *, Device *> m_bridgeSensorsDiscoveryRequests;
QHash<QNetworkReply *, Device *> m_bridgeSearchDevicesRequests;
QHash<QNetworkReply *, QPair<Device *, ActionId> > m_asyncActions;
QHash<HueBridge*, Device*> m_bridges;
QHash<HueLight*, Device*> m_lights;
QHash<HueBridge *, Device *> m_bridges;
QHash<HueLight *, Device *> m_lights;
QHash<HueRemote *, Device *> 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);
};

View File

@ -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"
}
]
}
]
}

View File

@ -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<QNetworkRequest, QByteArray> HueBridge::createDiscoverLightsRequest()
{
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + "/lights/"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return QPair<QNetworkRequest, QByteArray>(request, QByteArray());
}
QPair<QNetworkRequest, QByteArray> HueBridge::createSearchLightsRequest()
{
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + "/lights/"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return QPair<QNetworkRequest, QByteArray>(request, QByteArray());
}
QPair<QNetworkRequest, QByteArray> HueBridge::createSearchSensorsRequest()
{
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + apiKey() + "/sensors/"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return QPair<QNetworkRequest, QByteArray>(request, QByteArray());
}
QPair<QNetworkRequest, QByteArray> 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<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}

View File

@ -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<HueLight *> lights() const;
void addLight(HueLight *light);
QPair<QNetworkRequest, QByteArray> createDiscoverLightsRequest();
QPair<QNetworkRequest, QByteArray> createSearchLightsRequest();
QPair<QNetworkRequest, QByteArray> createSearchSensorsRequest();
QPair<QNetworkRequest, QByteArray> createCheckUpdatesRequest();
private:
QString m_id;
QString m_apiKey;
QHostAddress m_hostAddress;
QString m_name;

View File

@ -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;
}

View File

@ -0,0 +1,62 @@
#ifndef HUEDEVICE_H
#define HUEDEVICE_H
#include <QObject>
#include <QDebug>
#include <QHostAddress>
#include <QNetworkRequest>
#include <QJsonDocument>
#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

View File

@ -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<QNetworkRequest, QByteArray> 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<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
@ -317,8 +222,8 @@ QPair<QNetworkRequest, QByteArray> 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<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
@ -335,8 +240,8 @@ QPair<QNetworkRequest, QByteArray> 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<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
@ -352,8 +257,8 @@ QPair<QNetworkRequest, QByteArray> 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<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
@ -366,8 +271,8 @@ QPair<QNetworkRequest, QByteArray> 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<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
@ -382,7 +287,7 @@ QPair<QNetworkRequest, QByteArray> 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<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}

View File

@ -31,9 +31,9 @@
#include <QJsonDocument>
#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<QNetworkRequest, QByteArray> 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;

View File

@ -0,0 +1,43 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "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;
}

View File

@ -0,0 +1,56 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef HUEREMOTE_H
#define HUEREMOTE_H
#include <QObject>
#include <QDebug>
#include <QHostAddress>
#include <QNetworkRequest>
#include <QJsonDocument>
#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

View File

@ -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

View File

@ -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));