Updated philipshue plugin
This commit is contained in:
parent
bc3d2f3b4a
commit
6bcdc50d13
@ -74,18 +74,35 @@ void DevicePluginPhilipsHue::init()
|
||||
|
||||
}
|
||||
|
||||
Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
|
||||
void DevicePluginPhilipsHue::discoverDevices(DeviceDiscoveryInfo *info)
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
Q_UNUSED(deviceClassId)
|
||||
// We're starting a UpnpDiscovery and a NUpnpDiscovery.
|
||||
// For that, we create a tracking object holding pointers to both of those discoveries.
|
||||
// Both discoveries add their results to a temporary list.
|
||||
// Once a discovery is finished, it will remove itself from the tracking object.
|
||||
// When both discoveries are gone from the tracking object, the results are processed
|
||||
// deduped (a bridge can be found on both discovieries) and handed over to the DeviceDiscoveryInfo.
|
||||
|
||||
// Tracking object
|
||||
DiscoveryJob *discovery = new DiscoveryJob();
|
||||
m_discoveries.insert(info, discovery);
|
||||
|
||||
// clean up the discovery job when the DeviceDiscoveryInfo is destroyed (either finished or cancelled)
|
||||
connect(info, &DeviceDiscoveryInfo::destroyed, this, [this, info](){
|
||||
delete m_discoveries.take(info);
|
||||
});
|
||||
|
||||
|
||||
qCDebug(dcPhilipsHue()) << "Starting UPnP discovery...";
|
||||
UpnpDiscoveryReply *upnpReply = hardwareManager()->upnpDiscovery()->discoverDevices("libhue:idl");
|
||||
discovery->upnpReply = upnpReply;
|
||||
connect(upnpReply, &UpnpDiscoveryReply::finished, this, [this, upnpReply, discovery](){
|
||||
upnpReply->deleteLater();
|
||||
// Always clean up the upnp discovery
|
||||
connect(upnpReply, &UpnpDiscoveryReply::finished, upnpReply, &UpnpDiscoveryReply::deleteLater);
|
||||
|
||||
// Process results if info is still around
|
||||
connect(upnpReply, &UpnpDiscoveryReply::finished, info, [this, upnpReply, discovery](){
|
||||
|
||||
// Indicate we're done...
|
||||
discovery->upnpReply = nullptr;
|
||||
|
||||
if (upnpReply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
|
||||
@ -105,10 +122,6 @@ Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId
|
||||
}
|
||||
params.append(Param(bridgeDeviceHostParamTypeId, upnpDevice.hostAddress().toString()));
|
||||
params.append(Param(bridgeDeviceIdParamTypeId, bridgeId));
|
||||
// Not known yet...
|
||||
params.append(Param(bridgeDeviceApiKeyParamTypeId, QString()));
|
||||
params.append(Param(bridgeDeviceMacParamTypeId, QString()));
|
||||
params.append(Param(bridgeDeviceZigbeeChannelParamTypeId, -1));
|
||||
descriptor.setParams(params);
|
||||
qCDebug(dcPhilipsHue()) << "UPnP: Found Hue bridge:" << bridgeId;
|
||||
discovery->results.append(descriptor);
|
||||
@ -123,7 +136,12 @@ Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId
|
||||
QNetworkRequest request(QUrl("https://www.meethue.com/api/nupnp"));
|
||||
QNetworkReply *nUpnpReply = hardwareManager()->networkManager()->get(request);
|
||||
discovery->nUpnpReply = nUpnpReply;
|
||||
connect(nUpnpReply, &QNetworkReply::finished, this, [this, nUpnpReply, discovery](){
|
||||
|
||||
// Always clean up the network reply
|
||||
connect(nUpnpReply, &QNetworkReply::finished, nUpnpReply, &QNetworkReply::deleteLater);
|
||||
|
||||
// Process results if info is still around
|
||||
connect(nUpnpReply, &QNetworkReply::finished, info, [this, nUpnpReply, discovery](){
|
||||
nUpnpReply->deleteLater();
|
||||
discovery->nUpnpReply = nullptr;
|
||||
|
||||
@ -136,6 +154,7 @@ Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(nUpnpReply->readAll(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcPhilipsHue) << "N-UPNP discovery JSON error in response" << error.errorString();
|
||||
finishDiscovery(discovery);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -150,9 +169,6 @@ Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId
|
||||
}
|
||||
params.append(Param(bridgeDeviceHostParamTypeId, bridgeMap.value("internalipaddress").toString()));
|
||||
params.append(Param(bridgeDeviceIdParamTypeId, bridgeId));
|
||||
params.append(Param(bridgeDeviceApiKeyParamTypeId, QString()));
|
||||
params.append(Param(bridgeDeviceMacParamTypeId, QString()));
|
||||
params.append(Param(bridgeDeviceZigbeeChannelParamTypeId, -1));
|
||||
descriptor.setParams(params);
|
||||
qCDebug(dcPhilipsHue()) << "N-UPnP: Found Hue bridge:" << bridgeId;
|
||||
discovery->results.append(descriptor);
|
||||
@ -160,48 +176,93 @@ Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId
|
||||
|
||||
finishDiscovery(discovery);
|
||||
});
|
||||
|
||||
return Device::DeviceErrorAsync;
|
||||
}
|
||||
|
||||
Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||
void DevicePluginPhilipsHue::finishDiscovery(DevicePluginPhilipsHue::DiscoveryJob *job)
|
||||
{
|
||||
if (job->upnpReply || job->nUpnpReply) {
|
||||
// still pending...
|
||||
return;
|
||||
}
|
||||
QHash<QString, DeviceDescriptor> results;
|
||||
foreach (DeviceDescriptor result, job->results) {
|
||||
// dedupe
|
||||
QString bridgeId = result.params().paramValue(bridgeDeviceIdParamTypeId).toString();
|
||||
if (results.contains(bridgeId)) {
|
||||
qCDebug(dcPhilipsHue()) << "Discarding duplicate search result" << bridgeId;
|
||||
continue;
|
||||
}
|
||||
Device *dev = bridgeForBridgeId(bridgeId);
|
||||
if (dev) {
|
||||
qCDebug(dcPhilipsHue()) << "Bridge already added in system:" << bridgeId;
|
||||
result.setDeviceId(dev->id());
|
||||
}
|
||||
results.insert(bridgeId, result);
|
||||
|
||||
}
|
||||
|
||||
DeviceDiscoveryInfo *info = m_discoveries.key(job);
|
||||
|
||||
info->addDeviceDescriptors(results.values());
|
||||
info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::startPairing(DevicePairingInfo *info)
|
||||
{
|
||||
Q_ASSERT_X(info->deviceClassId() == bridgeDeviceClassId, "DevicePluginPhilipsHue::startPairing", "Unexpected device class.");
|
||||
|
||||
info->finish(Device::DeviceErrorNoError, QT_TR_NOOP("Please press the button on the Hue Bridge within 30 seconds before you continue"));
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::setupDevice(DeviceSetupInfo *info)
|
||||
{
|
||||
Device *device = info->device();
|
||||
|
||||
// Update the name on the bridge if the user changes the device name
|
||||
connect(device, &Device::nameChanged, this, &DevicePluginPhilipsHue::onDeviceNameChanged);
|
||||
|
||||
// hue bridge
|
||||
if (device->deviceClassId() == bridgeDeviceClassId) {
|
||||
// unconfigured bridges (from pairing)
|
||||
foreach (HueBridge *b, m_unconfiguredBridges) {
|
||||
if (b->hostAddress().toString() == device->paramValue(bridgeDeviceHostParamTypeId).toString()) {
|
||||
m_unconfiguredBridges.removeAll(b);
|
||||
qCDebug(dcPhilipsHue) << "Setup unconfigured Hue Bridge" << b->name();
|
||||
// Set data which was not known during discovery
|
||||
device->setParamValue(bridgeDeviceApiKeyParamTypeId, b->apiKey());
|
||||
device->setParamValue(bridgeDeviceZigbeeChannelParamTypeId, b->zigbeeChannel());
|
||||
device->setParamValue(bridgeDeviceMacParamTypeId, b->macAddress());
|
||||
m_bridges.insert(b, device);
|
||||
device->setStateValue(bridgeConnectedStateTypeId, true);
|
||||
discoverBridgeDevices(b);
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
// Loaded bridge
|
||||
qCDebug(dcPhilipsHue) << "Setup Hue Bridge" << device->params();
|
||||
|
||||
pluginStorage()->beginGroup(device->id().toString());
|
||||
QString apiKey = pluginStorage()->value("apiKey").toString();
|
||||
pluginStorage()->endGroup();
|
||||
|
||||
// For legacy reasons we might not have the api key in the pluginstorage yet. Check if there is a key in the device params.
|
||||
if (apiKey.isEmpty()) {
|
||||
qCWarning(dcPhilipsHue()) << "Loading api key from device params!";
|
||||
// Used to be in json, not any more.
|
||||
ParamTypeId bridgeDeviceApiKeyParamTypeId = ParamTypeId("{8bf5776a-d5a6-4600-8b27-481f0d803a8f}");
|
||||
apiKey = device->paramValue(bridgeDeviceApiKeyParamTypeId).toString();
|
||||
}
|
||||
|
||||
if (apiKey.isEmpty()) {
|
||||
qCWarning(dcPhilipsHue()) << "Failed to load api key";
|
||||
info->finish(Device::DeviceErrorAuthenticationFailure, QT_TR_NOOP("Not authenticated to bridge. Please reconfigure the device."));
|
||||
return;
|
||||
}
|
||||
|
||||
HueBridge *bridge = new HueBridge(this);
|
||||
bridge->setId(device->paramValue(bridgeDeviceIdParamTypeId).toString());
|
||||
bridge->setApiKey(device->paramValue(bridgeDeviceApiKeyParamTypeId).toString());
|
||||
bridge->setApiKey(apiKey);
|
||||
bridge->setHostAddress(QHostAddress(device->paramValue(bridgeDeviceHostParamTypeId).toString()));
|
||||
bridge->setMacAddress(device->paramValue(bridgeDeviceMacParamTypeId).toString());
|
||||
bridge->setZigbeeChannel(device->paramValue(bridgeDeviceZigbeeChannelParamTypeId).toInt());
|
||||
m_bridges.insert(bridge, device);
|
||||
|
||||
discoverBridgeDevices(bridge);
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
// At this point we need to have a bridge or we can't continue anyways
|
||||
HueBridge *bridge = m_bridges.key(myDevices().findById(device->parentId()));
|
||||
if (!bridge) {
|
||||
qCWarning(dcPhilipsHue()) << "No hue bridge set up. Cannot continue.";
|
||||
info->finish(Device::DeviceErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Hue color light
|
||||
if (device->deviceClassId() == colorLightDeviceClassId) {
|
||||
qCDebug(dcPhilipsHue) << "Setup Hue color light" << device->params();
|
||||
@ -220,7 +281,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||
|
||||
refreshLight(device);
|
||||
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
// Hue color temperature light
|
||||
@ -241,7 +302,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||
|
||||
refreshLight(device);
|
||||
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
// Hue white light
|
||||
@ -287,7 +348,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||
m_lights.insert(hueLight, device);
|
||||
refreshLight(device);
|
||||
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
// Hue remote
|
||||
@ -332,7 +393,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||
connect(hueRemote, &HueRemote::buttonPressed, this, &DevicePluginPhilipsHue::onRemoteButtonEvent);
|
||||
|
||||
m_remotes.insert(hueRemote, device);
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
// Hue tap
|
||||
@ -346,7 +407,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||
connect(hueTap, &HueRemote::buttonPressed, this, &DevicePluginPhilipsHue::onRemoteButtonEvent);
|
||||
|
||||
m_remotes.insert(hueTap, device);
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
// Hue Motion sensor
|
||||
@ -378,7 +439,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||
|
||||
m_motionSensors.insert(motionSensor, device);
|
||||
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
// Hue Outdoor sensor
|
||||
@ -410,12 +471,10 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
|
||||
|
||||
m_motionSensors.insert(outdoorSensor, device);
|
||||
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
qCWarning(dcPhilipsHue()) << "Unhandled setupDevice call" << device->deviceClassId();
|
||||
|
||||
return Device::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::deviceRemoved(Device *device)
|
||||
@ -456,31 +515,65 @@ void DevicePluginPhilipsHue::deviceRemoved(Device *device)
|
||||
}
|
||||
}
|
||||
|
||||
Device::DeviceSetupStatus DevicePluginPhilipsHue::confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret)
|
||||
void DevicePluginPhilipsHue::confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret)
|
||||
{
|
||||
Q_UNUSED(username)
|
||||
Q_UNUSED(secret)
|
||||
|
||||
qCDebug(dcPhilipsHue()) << "Confirming pairing for transactionId" << pairingTransactionId;
|
||||
if (deviceClassId != bridgeDeviceClassId)
|
||||
return Device::DeviceSetupStatusFailure;
|
||||
|
||||
PairingInfo *pairingInfo = new PairingInfo(this);
|
||||
pairingInfo->setPairingTransactionId(pairingTransactionId);
|
||||
pairingInfo->setHost(QHostAddress(params.paramValue(bridgeDeviceHostParamTypeId).toString()));
|
||||
|
||||
QVariantMap deviceTypeParam;
|
||||
deviceTypeParam.insert("devicetype", "nymea");
|
||||
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(deviceTypeParam);
|
||||
|
||||
QNetworkRequest request(QUrl("http://" + pairingInfo->host().toString() + "/api"));
|
||||
QString host = info->params().paramValue(bridgeDeviceHostParamTypeId).toString();
|
||||
QNetworkRequest request(QUrl("http://" + host + "/api"));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, jsonDoc.toJson());
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
|
||||
m_pairingRequests.insert(reply, pairingInfo);
|
||||
connect(reply, &QNetworkReply::finished, info, [this, info, reply](){
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Error connecting to hue bridge."));
|
||||
return;
|
||||
}
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
|
||||
return Device::DeviceSetupStatusAsync;
|
||||
// check JSON error
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString();
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Received unexpected data from hue bridge."));
|
||||
return;
|
||||
}
|
||||
|
||||
// check response error
|
||||
if (data.contains("error")) {
|
||||
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";
|
||||
}
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("An error happened pairing the hue bridge."));
|
||||
return;
|
||||
}
|
||||
|
||||
QString apiKey = jsonDoc.toVariant().toList().first().toMap().value("success").toMap().value("username").toString();
|
||||
|
||||
if (apiKey.isEmpty()) {
|
||||
qCWarning(dcPhilipsHue) << "Failed to pair Hue Bridge: did not get any key from the bridge";
|
||||
return info->finish(Device::DeviceErrorAuthenticationFailure, QT_TR_NOOP("The hue bridge has rejected the connection request."));
|
||||
}
|
||||
|
||||
qCDebug(dcPhilipsHue) << "Got api key from bridge:" << apiKey;
|
||||
|
||||
// All good. Store the API key
|
||||
pluginStorage()->beginGroup(info->deviceId().toString());
|
||||
pluginStorage()->setValue("apiKey", apiKey);
|
||||
pluginStorage()->endGroup();
|
||||
|
||||
info->finish(Device::DeviceErrorNoError);
|
||||
});
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::networkManagerReplyReady()
|
||||
@ -493,29 +586,7 @@ void DevicePluginPhilipsHue::networkManagerReplyReady()
|
||||
// qCDebug(dcPhilipsHue()) << "Hue reply:" << status << reply->error() << reply->errorString();
|
||||
|
||||
// create user finished
|
||||
if (m_pairingRequests.contains(reply)) {
|
||||
PairingInfo *pairingInfo = m_pairingRequests.take(reply);
|
||||
|
||||
// check HTTP status code
|
||||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcPhilipsHue) << "Request error:" << status << reply->errorString();
|
||||
pairingInfo->deleteLater();
|
||||
return;
|
||||
}
|
||||
processPairingResponse(pairingInfo, reply->readAll());
|
||||
|
||||
} else if (m_informationRequests.contains(reply)) {
|
||||
PairingInfo *pairingInfo = m_informationRequests.take(reply);
|
||||
|
||||
// check HTTP status code
|
||||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcPhilipsHue) << "Request error:" << status << reply->errorString();
|
||||
pairingInfo->deleteLater();
|
||||
return;
|
||||
}
|
||||
processInformationResponse(pairingInfo, reply->readAll());
|
||||
|
||||
} else if (m_bridgeLightsDiscoveryRequests.contains(reply)) {
|
||||
if (m_bridgeLightsDiscoveryRequests.contains(reply)) {
|
||||
Device *device = m_bridgeLightsDiscoveryRequests.take(reply);
|
||||
|
||||
// check HTTP status code
|
||||
@ -598,18 +669,6 @@ void DevicePluginPhilipsHue::networkManagerReplyReady()
|
||||
}
|
||||
processSensorsRefreshResponse(device, reply->readAll());
|
||||
|
||||
} else if (m_asyncActions.contains(reply)) {
|
||||
QPair<Device *, ActionId> actionInfo = m_asyncActions.take(reply);
|
||||
|
||||
// check HTTP status code
|
||||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcPhilipsHue) << "Execute Hue Light action request error:" << status << reply->errorString();
|
||||
bridgeReachableChanged(actionInfo.first, false);
|
||||
emit actionExecutionFinished(actionInfo.second, Device::DeviceErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
processActionResponse(actionInfo.first, actionInfo.second, reply->readAll());
|
||||
|
||||
} else if (m_setNameRequests.contains(reply)) {
|
||||
Device *device = m_setNameRequests.take(reply);
|
||||
|
||||
@ -637,151 +696,71 @@ void DevicePluginPhilipsHue::onDeviceNameChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::finishDiscovery(DevicePluginPhilipsHue::DiscoveryJob *job)
|
||||
void DevicePluginPhilipsHue::executeAction(DeviceActionInfo *info)
|
||||
{
|
||||
if (job->upnpReply || job->nUpnpReply) {
|
||||
// still pending...
|
||||
return;
|
||||
}
|
||||
QHash<QString, DeviceDescriptor> results;
|
||||
foreach (DeviceDescriptor result, job->results) {
|
||||
// dedupe
|
||||
QString bridgeId = result.params().paramValue(bridgeDeviceIdParamTypeId).toString();
|
||||
if (results.contains(bridgeId)) {
|
||||
qCDebug(dcPhilipsHue()) << "Discarding duplicate search result" << bridgeId;
|
||||
continue;
|
||||
}
|
||||
Device *dev = bridgeForBridgeId(bridgeId);
|
||||
if (dev) {
|
||||
qCDebug(dcPhilipsHue()) << "Bridge already added in system:" << bridgeId;
|
||||
result.setDeviceId(dev->id());
|
||||
}
|
||||
results.insert(bridgeId, result);
|
||||
Device *device = info->device();
|
||||
Action action = info->action();
|
||||
|
||||
}
|
||||
emit devicesDiscovered(bridgeDeviceClassId, results.values());
|
||||
delete job;
|
||||
}
|
||||
|
||||
Device::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
qCDebug(dcPhilipsHue) << "Execute action" << action.actionTypeId() << action.params();
|
||||
|
||||
// Color light
|
||||
if (device->deviceClassId() == colorLightDeviceClassId) {
|
||||
QNetworkReply *reply = nullptr;
|
||||
|
||||
// lights
|
||||
if (device->deviceClassId() == colorLightDeviceClassId ||
|
||||
device->deviceClassId() == colorTemperatureLightDeviceClassId ||
|
||||
device->deviceClassId() == dimmableLightDeviceClassId) {
|
||||
|
||||
HueLight *light = m_lights.key(device);
|
||||
|
||||
if (!light->reachable()) {
|
||||
qCWarning(dcPhilipsHue) << "Light" << light->name() << "not reachable";
|
||||
return Device::DeviceErrorHardwareNotAvailable;
|
||||
return info->finish(Device::DeviceErrorHardwareNotAvailable);
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == colorLightPowerActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetPowerRequest(action.param(colorLightPowerActionPowerParamTypeId).value().toBool());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == colorLightColorActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetColorRequest(action.param(colorLightColorActionColorParamTypeId).value().value<QColor>());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply,QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == colorLightBrightnessActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetBrightnessRequest(percentageToBrightness(action.param(colorLightBrightnessActionBrightnessParamTypeId).value().toInt()));
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == colorLightEffectActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetEffectRequest(action.param(colorLightEffectActionEffectParamTypeId).value().toString());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == colorLightAlertActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createFlashRequest(action.param(colorLightAlertActionAlertParamTypeId).value().toString());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == colorLightColorTemperatureActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetTemperatureRequest(action.param(colorLightColorTemperatureActionColorTemperatureParamTypeId).value().toInt());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
}
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
// Color temperature light
|
||||
if (device->deviceClassId() == colorTemperatureLightDeviceClassId) {
|
||||
HueLight *light = m_lights.key(device);
|
||||
|
||||
if (!light->reachable()) {
|
||||
qCWarning(dcPhilipsHue) << "Light" << light->name() << "not reachable";
|
||||
return Device::DeviceErrorHardwareNotAvailable;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == colorTemperatureLightPowerActionTypeId) {
|
||||
// Color temperature light
|
||||
else if (action.actionTypeId() == colorTemperatureLightPowerActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetPowerRequest(action.param(colorTemperatureLightPowerActionPowerParamTypeId).value().toBool());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == colorTemperatureLightBrightnessActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetBrightnessRequest(percentageToBrightness(action.param(colorTemperatureLightBrightnessActionBrightnessParamTypeId).value().toInt()));
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == colorTemperatureLightAlertActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createFlashRequest(action.param(colorTemperatureLightAlertActionAlertParamTypeId).value().toString());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == colorTemperatureLightColorTemperatureActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetTemperatureRequest(action.param(colorTemperatureLightColorTemperatureActionColorTemperatureParamTypeId).value().toInt());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
}
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
// Dimmable light
|
||||
if (device->deviceClassId() == dimmableLightDeviceClassId) {
|
||||
HueLight *light = m_lights.key(device);
|
||||
|
||||
if (!light->reachable()) {
|
||||
qCWarning(dcPhilipsHue) << "Light" << light->name() << "not reachable";
|
||||
return Device::DeviceErrorHardwareNotAvailable;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == dimmableLightPowerActionTypeId) {
|
||||
// Dimmable light
|
||||
else if (action.actionTypeId() == dimmableLightPowerActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetPowerRequest(action.param(dimmableLightPowerActionPowerParamTypeId).value().toBool());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == dimmableLightBrightnessActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createSetBrightnessRequest(percentageToBrightness(action.param(dimmableLightBrightnessActionBrightnessParamTypeId).value().toInt()));
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == dimmableLightAlertActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = light->createFlashRequest(action.param(dimmableLightAlertActionAlertParamTypeId).value().toString());
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
}
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
// Hue bridge
|
||||
@ -789,28 +768,64 @@ Device::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const
|
||||
HueBridge *bridge = m_bridges.key(device);
|
||||
if (!device->stateValue(bridgeConnectedStateTypeId).toBool()) {
|
||||
qCWarning(dcPhilipsHue) << "Bridge" << bridge->hostAddress().toString() << "not reachable";
|
||||
return Device::DeviceErrorHardwareNotAvailable;
|
||||
return info->finish(Device::DeviceErrorHardwareNotAvailable);
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == bridgeSearchNewDevicesActionTypeId) {
|
||||
searchNewDevices(bridge, action.param(bridgeSearchNewDevicesActionSerialParamTypeId).value().toString());
|
||||
return Device::DeviceErrorNoError;
|
||||
return info->finish(Device::DeviceErrorNoError);
|
||||
} else if (action.actionTypeId() == bridgeCheckForUpdatesActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = bridge->createCheckUpdatesRequest();
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
} else if (action.actionTypeId() == bridgeUpgradeActionTypeId) {
|
||||
QPair<QNetworkRequest, QByteArray> request = bridge->createUpgradeRequest();
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_asyncActions.insert(reply, QPair<Device *, ActionId>(device, action.id()));
|
||||
return Device::DeviceErrorAsync;
|
||||
reply = hardwareManager()->networkManager()->put(request.first, request.second);
|
||||
}
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
return Device::DeviceErrorDeviceClassNotFound;
|
||||
|
||||
if (!reply) {
|
||||
qCWarning(dcPhilipsHue()) << "Unhandled Hue action! Plugin bug!";
|
||||
Q_ASSERT_X(false, "HuePlugin", "Unhandled action");
|
||||
info->finish(Device::DeviceErrorUnsupportedFeature);
|
||||
return;
|
||||
}
|
||||
|
||||
// Always clean up the reply when it finishes
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
|
||||
// Handle response if info is still around
|
||||
connect(reply, &QNetworkReply::finished, info, [this, info, reply](){
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Error sending command to hue bridge."));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString();
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Received unexpected data from hue bridge."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.contains("error")) {
|
||||
if (!jsonDoc.toVariant().toList().isEmpty()) {
|
||||
qCWarning(dcPhilipsHue) << "Failed to execute Hue action:" << jsonDoc.toJson(); //jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString();
|
||||
} else {
|
||||
qCWarning(dcPhilipsHue) << "Failed to execute Hue action: Invalid error message format";
|
||||
}
|
||||
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("An unexpected error happened when sending the command to the hue bridge."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->device()->deviceClassId() != bridgeDeviceClassId) {
|
||||
m_lights.key(info->device())->processActionResponse(jsonDoc.toVariant().toList());
|
||||
}
|
||||
|
||||
info->finish(Device::DeviceErrorNoError);
|
||||
});
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::lightStateChanged()
|
||||
@ -1089,9 +1104,7 @@ void DevicePluginPhilipsHue::processBridgeLightDiscoveryResponse(Device *device,
|
||||
}
|
||||
|
||||
// Create Lights if not already added
|
||||
QList<DeviceDescriptor> colorLightDescriptors;
|
||||
QList<DeviceDescriptor> colorTemperatureLightDescriptors;
|
||||
QList<DeviceDescriptor> dimmableLightDescriptors;
|
||||
DeviceDescriptors descriptors;
|
||||
|
||||
QVariantMap lightsMap = jsonDoc.toVariant().toMap();
|
||||
foreach (QString lightId, lightsMap.keys()) {
|
||||
@ -1111,7 +1124,7 @@ void DevicePluginPhilipsHue::processBridgeLightDiscoveryResponse(Device *device,
|
||||
params.append(Param(dimmableLightDeviceUuidParamTypeId, uuid));
|
||||
params.append(Param(dimmableLightDeviceLightIdParamTypeId, lightId));
|
||||
descriptor.setParams(params);
|
||||
dimmableLightDescriptors.append(descriptor);
|
||||
descriptors.append(descriptor);
|
||||
|
||||
qCDebug(dcPhilipsHue) << "Found new dimmable light" << lightMap.value("name").toString() << model;
|
||||
} else if (lightMap.value("type").toString() == "Color temperature light") {
|
||||
@ -1122,7 +1135,7 @@ void DevicePluginPhilipsHue::processBridgeLightDiscoveryResponse(Device *device,
|
||||
params.append(Param(colorTemperatureLightDeviceUuidParamTypeId, uuid));
|
||||
params.append(Param(colorTemperatureLightDeviceLightIdParamTypeId, lightId));
|
||||
descriptor.setParams(params);
|
||||
colorTemperatureLightDescriptors.append(descriptor);
|
||||
descriptors.append(descriptor);
|
||||
|
||||
qCDebug(dcPhilipsHue) << "Found new color temperature light" << lightMap.value("name").toString() << model;
|
||||
} else {
|
||||
@ -1133,17 +1146,14 @@ void DevicePluginPhilipsHue::processBridgeLightDiscoveryResponse(Device *device,
|
||||
params.append(Param(colorLightDeviceUuidParamTypeId, uuid));
|
||||
params.append(Param(colorLightDeviceLightIdParamTypeId, lightId));
|
||||
descriptor.setParams(params);
|
||||
colorLightDescriptors.append(descriptor);
|
||||
descriptors.append(descriptor);
|
||||
qCDebug(dcPhilipsHue) << "Found new color light" << lightMap.value("name").toString() << model;
|
||||
}
|
||||
}
|
||||
|
||||
if (!colorLightDescriptors.isEmpty())
|
||||
emit autoDevicesAppeared(colorLightDeviceClassId, colorLightDescriptors);
|
||||
if (!colorTemperatureLightDescriptors.isEmpty())
|
||||
emit autoDevicesAppeared(colorTemperatureLightDeviceClassId, colorTemperatureLightDescriptors);
|
||||
if (!dimmableLightDescriptors.isEmpty())
|
||||
emit autoDevicesAppeared(dimmableLightDeviceClassId, dimmableLightDescriptors);
|
||||
if (!descriptors.isEmpty()) {
|
||||
emit autoDevicesAppeared(descriptors);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device, const QByteArray &data)
|
||||
@ -1187,7 +1197,7 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device
|
||||
params.append(Param(remoteDeviceUuidParamTypeId, uuid));
|
||||
params.append(Param(remoteDeviceSensorIdParamTypeId, sensorId));
|
||||
descriptor.setParams(params);
|
||||
emit autoDevicesAppeared(remoteDeviceClassId, {descriptor});
|
||||
emit autoDevicesAppeared({descriptor});
|
||||
qCDebug(dcPhilipsHue) << "Found new remote" << sensorMap.value("name").toString() << model;
|
||||
} else if (sensorMap.value("type").toString() == "ZGPSwitch") {
|
||||
DeviceDescriptor descriptor(tapDeviceClassId, sensorMap.value("name").toString(), "Philips Hue Tap", device->id());
|
||||
@ -1196,7 +1206,7 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device
|
||||
params.append(Param(tapDeviceModelIdParamTypeId, model));
|
||||
params.append(Param(tapDeviceSensorIdParamTypeId, sensorId));
|
||||
descriptor.setParams(params);
|
||||
emit autoDevicesAppeared(tapDeviceClassId, {descriptor});
|
||||
emit autoDevicesAppeared({descriptor});
|
||||
qCDebug(dcPhilipsHue()) << "Found hue tap:" << sensorMap << tapDeviceClassId;
|
||||
|
||||
} else if (model == "SML001" || model == "SML002") {
|
||||
@ -1294,7 +1304,7 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device
|
||||
params.append(Param(motionSensorDeviceSensorIdLightParamTypeId, motionSensor->lightSensorId()));
|
||||
descriptor.setParams(params);
|
||||
qCDebug(dcPhilipsHue()) << "Found new motion sensor" << baseUuid << motionSensorDeviceClassId;
|
||||
emit autoDevicesAppeared(motionSensorDeviceClassId, {descriptor});
|
||||
emit autoDevicesAppeared({descriptor});
|
||||
} else if (motionSensor->modelId() == "SML002") {
|
||||
DeviceDescriptor descriptor(outdoorSensorDeviceClassId, tr("Philips Hue Outdoor sensor"), baseUuid, device->id());
|
||||
ParamList params;
|
||||
@ -1308,7 +1318,7 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device
|
||||
params.append(Param(outdoorSensorDeviceSensorIdLightParamTypeId, motionSensor->lightSensorId()));
|
||||
descriptor.setParams(params);
|
||||
qCDebug(dcPhilipsHue()) << "Found new outdoor sensor" << baseUuid << outdoorSensorDeviceClassId;
|
||||
emit autoDevicesAppeared(outdoorSensorDeviceClassId, {descriptor});
|
||||
emit autoDevicesAppeared({descriptor});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1468,7 +1478,6 @@ void DevicePluginPhilipsHue::processSetNameResponse(Device *device, const QByteA
|
||||
// check JSON error
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString();
|
||||
emit deviceSetupFinished(device, Device::DeviceSetupStatusFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1479,7 +1488,6 @@ void DevicePluginPhilipsHue::processSetNameResponse(Device *device, const QByteA
|
||||
} else {
|
||||
qCWarning(dcPhilipsHue) << "Failed to set name of Hue: Invalid error message format";
|
||||
}
|
||||
emit deviceSetupFinished(device, Device::DeviceSetupStatusFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1488,119 +1496,6 @@ void DevicePluginPhilipsHue::processSetNameResponse(Device *device, const QByteA
|
||||
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::processPairingResponse(PairingInfo *pairingInfo, 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();
|
||||
emit pairingFinished(pairingInfo->pairingTransactionId(), Device::DeviceSetupStatusFailure);
|
||||
pairingInfo->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
// check response error
|
||||
if (data.contains("error")) {
|
||||
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(), Device::DeviceSetupStatusFailure);
|
||||
pairingInfo->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
pairingInfo->setApiKey(jsonDoc.toVariant().toList().first().toMap().value("success").toMap().value("username").toString());
|
||||
|
||||
qCDebug(dcPhilipsHue) << "Got api key from bridge:" << pairingInfo->apiKey();
|
||||
|
||||
if (pairingInfo->apiKey().isEmpty()) {
|
||||
qCWarning(dcPhilipsHue) << "Failed to pair Hue Bridge: did not get any key from the bridge";
|
||||
emit pairingFinished(pairingInfo->pairingTransactionId(), Device::DeviceSetupStatusFailure);
|
||||
pairingInfo->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
// Paired successfully, check bridge information
|
||||
QNetworkRequest request(QUrl("http://" + pairingInfo->host().toString() + "/api/" + pairingInfo->apiKey() + "/config"));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
|
||||
m_informationRequests.insert(reply, pairingInfo);
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::processInformationResponse(PairingInfo *pairingInfo, 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();
|
||||
emit pairingFinished(pairingInfo->pairingTransactionId(), Device::DeviceSetupStatusFailure);
|
||||
pairingInfo->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||
|
||||
// check response error
|
||||
if (response.contains("error")) {
|
||||
qCWarning(dcPhilipsHue) << "Failed to get information from Hue Bridge:" << response.value("error").toMap().value("description").toString();
|
||||
emit pairingFinished(pairingInfo->pairingTransactionId(), Device::DeviceSetupStatusFailure);
|
||||
pairingInfo->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
// create Bridge
|
||||
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);
|
||||
|
||||
emit pairingFinished(pairingInfo->pairingTransactionId(), Device::DeviceSetupStatusSuccess);
|
||||
pairingInfo->deleteLater();
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::processActionResponse(Device *device, const ActionId actionId, 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();
|
||||
emit actionExecutionFinished(actionId, Device::DeviceErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
// check response error
|
||||
if (data.contains("error")) {
|
||||
if (!jsonDoc.toVariant().toList().isEmpty()) {
|
||||
qCWarning(dcPhilipsHue) << "Failed to execute Hue action:" << jsonDoc.toJson(); //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, Device::DeviceErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device->deviceClassId() != bridgeDeviceClassId)
|
||||
m_lights.key(device)->processActionResponse(jsonDoc.toVariant().toList());
|
||||
|
||||
emit actionExecutionFinished(actionId, Device::DeviceErrorNoError);
|
||||
}
|
||||
|
||||
void DevicePluginPhilipsHue::bridgeReachableChanged(Device *device, const bool &reachable)
|
||||
{
|
||||
if (reachable) {
|
||||
|
||||
@ -28,7 +28,6 @@
|
||||
#include "huebridge.h"
|
||||
#include "huelight.h"
|
||||
#include "hueremote.h"
|
||||
#include "pairinginfo.h"
|
||||
#include "huemotionsensor.h"
|
||||
#include "huemotionsensor.h"
|
||||
|
||||
@ -50,13 +49,12 @@ public:
|
||||
~DevicePluginPhilipsHue();
|
||||
|
||||
void init() override;
|
||||
Device::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
|
||||
void discoverDevices(DeviceDiscoveryInfo *info) override;
|
||||
void startPairing(DevicePairingInfo *info) override;
|
||||
void confirmPairing(DevicePairingInfo *info, const QString &username, const QString &secret) override;
|
||||
void setupDevice(DeviceSetupInfo *info) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
Device::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret) override;
|
||||
|
||||
public slots:
|
||||
Device::DeviceError executeAction(Device *device, const Action &action);
|
||||
void executeAction(DeviceActionInfo *info) override;
|
||||
|
||||
private slots:
|
||||
void lightStateChanged();
|
||||
@ -79,18 +77,15 @@ private:
|
||||
public:
|
||||
UpnpDiscoveryReply* upnpReply;
|
||||
QNetworkReply* nUpnpReply;
|
||||
QList<DeviceDescriptor> results;
|
||||
DeviceDescriptors results;
|
||||
};
|
||||
QHash<DeviceDiscoveryInfo*, DiscoveryJob*> m_discoveries;
|
||||
void finishDiscovery(DiscoveryJob* job);
|
||||
|
||||
PluginTimer *m_pluginTimer1Sec = nullptr;
|
||||
PluginTimer *m_pluginTimer5Sec = nullptr;
|
||||
PluginTimer *m_pluginTimer15Sec = nullptr;
|
||||
|
||||
QHash<QNetworkReply *, PairingInfo *> m_pairingRequests;
|
||||
QHash<QNetworkReply *, PairingInfo *> m_informationRequests;
|
||||
|
||||
QList<HueBridge *> m_unconfiguredBridges;
|
||||
QList<HueLight *> m_unconfiguredLights;
|
||||
|
||||
QHash<QNetworkReply *, Device *> m_lightRefreshRequests;
|
||||
@ -129,8 +124,6 @@ private:
|
||||
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);
|
||||
void processActionResponse(Device *device, const ActionId actionId, const QByteArray &data);
|
||||
|
||||
void bridgeReachableChanged(Device *device, const bool &reachable);
|
||||
|
||||
@ -15,16 +15,7 @@
|
||||
"interfaces": ["gateway"],
|
||||
"createMethods": ["discovery"],
|
||||
"setupMethod": "pushButton",
|
||||
"pairingInfo": "Please press the button on the Hue Bridge within 30 seconds before you continue",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "8bf5776a-d5a6-4600-8b27-481f0d803a8f",
|
||||
"name": "apiKey",
|
||||
"displayName": "api key",
|
||||
"type" : "QString",
|
||||
"inputType": "TextLine",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "1845975b-1184-4440-bc0d-73d53a9f683c",
|
||||
"name": "host",
|
||||
@ -33,34 +24,12 @@
|
||||
"inputType": "IPv4Address",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "2c67203d-a308-45ec-9a08-fc4183c06ff8",
|
||||
"name": "mac",
|
||||
"displayName": "mac address",
|
||||
"type" : "QString",
|
||||
"inputType": "MacAddress",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "a496feb0-3b7b-46cb-a63a-e063447d6b1d",
|
||||
"name": "id",
|
||||
"displayName": "id",
|
||||
"type" : "QString",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "ea228c4d-975c-4b43-9445-7c9a907c29d6",
|
||||
"name": "softwareVersion",
|
||||
"displayName": "software version",
|
||||
"type" : "QString",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "53170394-956c-4511-b3a8-2c8a502ef1ed",
|
||||
"name": "zigbeeChannel",
|
||||
"displayName": "zigbee channel",
|
||||
"type" : "int",
|
||||
"readOnly": true
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
@ -70,7 +39,8 @@
|
||||
"displayName": "reachable",
|
||||
"displayNameEvent": "reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "7a230e89-c4ce-4276-90e0-6a9ddb890603",
|
||||
@ -172,7 +142,8 @@
|
||||
"displayName": "reachable",
|
||||
"displayNameEvent": "reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "90aaffe5-6a76-47d2-a14a-550f60390245",
|
||||
@ -300,7 +271,8 @@
|
||||
"displayName": "reachable",
|
||||
"displayNameEvent": "reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "4e579f6a-e4b3-4876-804a-9fcc066f90f9",
|
||||
@ -403,7 +375,8 @@
|
||||
"displayName": "reachable",
|
||||
"displayNameEvent": "reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "5995ecb7-b5e5-4f6a-b4d6-33c93497e5fb",
|
||||
@ -494,7 +467,8 @@
|
||||
"displayName": "reachable",
|
||||
"displayNameEvent": "reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "683e493a-9796-4d5e-b0e3-61cb178d5819",
|
||||
@ -590,7 +564,8 @@
|
||||
"displayName": "reachable",
|
||||
"displayNameEvent": "reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
}
|
||||
],
|
||||
"eventTypes": [
|
||||
@ -693,7 +668,8 @@
|
||||
"displayName": "Reachable",
|
||||
"displayNameEvent": "Reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "19b18531-61e5-4998-89d1-765d740e24eb",
|
||||
@ -834,7 +810,8 @@
|
||||
"displayName": "Reachable",
|
||||
"displayNameEvent": "Reachable changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
"type": "bool",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "ac463b30-24af-4352-84da-19a3ffc906bd",
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "pairinginfo.h"
|
||||
|
||||
PairingInfo::PairingInfo(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
PairingTransactionId PairingInfo::pairingTransactionId() const
|
||||
{
|
||||
return m_pairingTransactionId;
|
||||
}
|
||||
|
||||
void PairingInfo::setPairingTransactionId(const PairingTransactionId &pairingTransactionId)
|
||||
{
|
||||
m_pairingTransactionId = pairingTransactionId;
|
||||
}
|
||||
|
||||
QHostAddress PairingInfo::host() const
|
||||
{
|
||||
return m_host;
|
||||
}
|
||||
|
||||
void PairingInfo::setHost(const QHostAddress &host)
|
||||
{
|
||||
m_host = host;
|
||||
}
|
||||
|
||||
QString PairingInfo::apiKey() const
|
||||
{
|
||||
return m_apiKey;
|
||||
}
|
||||
|
||||
void PairingInfo::setApiKey(const QString &apiKey)
|
||||
{
|
||||
m_apiKey = apiKey;
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef PAIRINGINFO_H
|
||||
#define PAIRINGINFO_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
|
||||
#include "typeutils.h"
|
||||
|
||||
class PairingInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PairingInfo(QObject *parent = 0);
|
||||
|
||||
PairingTransactionId pairingTransactionId() const;
|
||||
void setPairingTransactionId(const PairingTransactionId &pairingTransactionId);
|
||||
|
||||
QHostAddress host() const;
|
||||
void setHost(const QHostAddress &host);
|
||||
|
||||
QString apiKey() const;
|
||||
void setApiKey(const QString &apiKey);
|
||||
|
||||
private:
|
||||
PairingTransactionId m_pairingTransactionId;
|
||||
QHostAddress m_host;
|
||||
QString m_apiKey;
|
||||
};
|
||||
|
||||
#endif // PAIRINGINFO_H
|
||||
@ -11,7 +11,6 @@ SOURCES += \
|
||||
huebridge.cpp \
|
||||
huelight.cpp \
|
||||
huemotionsensor.cpp \
|
||||
pairinginfo.cpp \
|
||||
hueremote.cpp \
|
||||
huedevice.cpp
|
||||
|
||||
@ -23,7 +22,6 @@ HEADERS += \
|
||||
huebridge.h \
|
||||
huelight.h \
|
||||
huemotionsensor.h \
|
||||
pairinginfo.h \
|
||||
hueremote.h \
|
||||
huedevice.h
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user