update hue plugin

This commit is contained in:
Simon Stürz 2015-06-17 10:55:22 +02:00 committed by Michael Zanetti
parent 5ac6509a6a
commit 05306f5c90
16 changed files with 1195 additions and 873 deletions

View File

@ -2,7 +2,7 @@
GUH_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') GUH_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"')
# define JSON protocol version # define JSON protocol version
JSON_PROTOCOL_VERSION=25 JSON_PROTOCOL_VERSION=26
DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" JSON_PROTOCOL_VERSION=\\\"$${JSON_PROTOCOL_VERSION}\\\" DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" JSON_PROTOCOL_VERSION=\\\"$${JSON_PROTOCOL_VERSION}\\\"
@ -25,4 +25,3 @@ enable433gpio {
top_srcdir=$$PWD top_srcdir=$$PWD
top_builddir=$$shadowed($$PWD) top_builddir=$$shadowed($$PWD)

View File

@ -624,11 +624,22 @@ DeviceManager::DeviceError DeviceManager::removeConfiguredDevice(const DeviceId
m_configuredDevices.removeAll(device); m_configuredDevices.removeAll(device);
m_devicePlugins.value(device->pluginId())->deviceRemoved(device); m_devicePlugins.value(device->pluginId())->deviceRemoved(device);
m_pluginTimerUsers.removeAll(device); // check if this plugin still needs the guhTimer call
if (m_pluginTimerUsers.isEmpty()) { bool pluginNeedsTimer = false;
m_pluginTimer.stop(); foreach (Device* d, m_configuredDevices) {
if (d->pluginId() == device->pluginId()) {
pluginNeedsTimer = true;
break;
}
} }
// if this plugin doesn't need any longer the guhTimer call
if (!pluginNeedsTimer) {
m_pluginTimerUsers.removeAll(plugin(device->pluginId()));
if (m_pluginTimerUsers.isEmpty()) {
m_pluginTimer.stop();
}
}
device->deleteLater(); device->deleteLater();
QSettings settings(m_settingsFile); QSettings settings(m_settingsFile);
@ -929,7 +940,9 @@ void DeviceManager::slotDeviceSetupFinished(Device *device, DeviceManager::Devic
// Additionally fire off one event to initialize stuff // Additionally fire off one event to initialize stuff
QTimer::singleShot(0, this, SLOT(timerEvent())); QTimer::singleShot(0, this, SLOT(timerEvent()));
} }
m_pluginTimerUsers.append(device); if (!m_pluginTimerUsers.contains(plugin)) {
m_pluginTimerUsers.append(plugin);
}
} }
// if this is a async device edit result // if this is a async device edit result
@ -1123,10 +1136,8 @@ void DeviceManager::upnpNotifyReceived(const QByteArray &notifyData)
void DeviceManager::timerEvent() void DeviceManager::timerEvent()
{ {
foreach (Device *device, m_configuredDevices) { foreach (DevicePlugin *plugin, m_pluginTimerUsers) {
DeviceClass deviceClass = m_supportedDevices.value(device->deviceClassId()); if (plugin->requiredHardware().testFlag(HardwareResourceTimer)) {
DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId());
if (plugin && plugin->requiredHardware().testFlag(HardwareResourceTimer)) {
plugin->guhTimer(); plugin->guhTimer();
} }
} }
@ -1175,7 +1186,9 @@ DeviceManager::DeviceSetupStatus DeviceManager::setupDevice(Device *device)
// Additionally fire off one event to initialize stuff // Additionally fire off one event to initialize stuff
QTimer::singleShot(0, this, SLOT(timerEvent())); QTimer::singleShot(0, this, SLOT(timerEvent()));
} }
m_pluginTimerUsers.append(device); if (!m_pluginTimerUsers.contains(plugin)) {
m_pluginTimerUsers.append(plugin);
}
} }
connect(device, SIGNAL(stateValueChanged(QUuid,QVariant)), this, SLOT(slotDeviceStateValueChanged(QUuid,QVariant))); connect(device, SIGNAL(stateValueChanged(QUuid,QVariant)), this, SLOT(slotDeviceStateValueChanged(QUuid,QVariant)));

View File

@ -158,7 +158,7 @@ private:
QHash<VendorId, Vendor> m_supportedVendors; QHash<VendorId, Vendor> m_supportedVendors;
QHash<VendorId, QList<DeviceClassId> > m_vendorDeviceMap; QHash<VendorId, QList<DeviceClassId> > m_vendorDeviceMap;
QHash<DeviceClassId, DeviceClass> m_supportedDevices; QHash<DeviceClassId, DeviceClass> m_supportedDevices;
QList<Device*> m_configuredDevices; QList<Device *> m_configuredDevices;
QHash<DeviceDescriptorId, DeviceDescriptor> m_discoveredDevices; QHash<DeviceDescriptorId, DeviceDescriptor> m_discoveredDevices;
QHash<PluginId, DevicePlugin*> m_devicePlugins; QHash<PluginId, DevicePlugin*> m_devicePlugins;
@ -168,15 +168,15 @@ private:
// Hardware Resources // Hardware Resources
Radio433* m_radio433; Radio433* m_radio433;
QTimer m_pluginTimer; QTimer m_pluginTimer;
QList<Device*> m_pluginTimerUsers; QList<DevicePlugin *> m_pluginTimerUsers;
NetworkManager *m_networkManager; NetworkManager *m_networkManager;
QHash<QUuid, QPair<DeviceClassId, ParamList> > m_pairingsJustAdd; QHash<QUuid, QPair<DeviceClassId, ParamList> > m_pairingsJustAdd;
QHash<QUuid, QPair<DeviceClassId, DeviceDescriptorId> > m_pairingsDiscovery; QHash<QUuid, QPair<DeviceClassId, DeviceDescriptorId> > m_pairingsDiscovery;
QList<Device*> m_asyncDeviceEdit; QList<Device *> m_asyncDeviceEdit;
QList<DevicePlugin*> m_discoveringPlugins; QList<DevicePlugin *> m_discoveringPlugins;
friend class DevicePlugin; friend class DevicePlugin;
}; };

View File

@ -247,7 +247,13 @@ QList<DeviceClass> DevicePlugin::supportedDevices() const
actionType.setName("set " + st.value("name").toString()); actionType.setName("set " + st.value("name").toString());
// Note: fields already checked in StateType // Note: fields already checked in StateType
ParamType paramType(st.value("name").toString(), t, st.value("defaultValue").toVariant()); ParamType paramType(st.value("name").toString(), t, st.value("defaultValue").toVariant());
// states don't have allowed values if (st.contains("allowedValues")) {
QVariantList allowedValues;
foreach (const QJsonValue &allowedTypesJson, st.value("allowedValues").toArray()) {
allowedValues.append(allowedTypesJson.toVariant());
}
paramType.setAllowedValues(allowedValues);
}
// states don't have input types // states don't have input types
paramType.setUnit(unitStringToUnit(st.value("unit").toString())); paramType.setUnit(unitStringToUnit(st.value("unit").toString()));
paramType.setLimits(st.value("minValue").toVariant(), st.value("maxValue").toVariant()); paramType.setLimits(st.value("minValue").toVariant(), st.value("maxValue").toVariant());
@ -667,6 +673,8 @@ Types::Unit DevicePlugin::unitStringToUnit(const QString &unitString) const
return Types::UnitDegreeCelsius; return Types::UnitDegreeCelsius;
} else if (unitString == "DegreeKelvin") { } else if (unitString == "DegreeKelvin") {
return Types::UnitDegreeKelvin; return Types::UnitDegreeKelvin;
} else if (unitString == "Mired") {
return Types::UnitMired;
} else if (unitString == "MilliBar") { } else if (unitString == "MilliBar") {
return Types::UnitMilliBar; return Types::UnitMilliBar;
} else if (unitString == "Bar") { } else if (unitString == "Bar") {

View File

@ -89,6 +89,7 @@ public:
UnitRadiant, UnitRadiant,
UnitDegreeCelsius, UnitDegreeCelsius,
UnitDegreeKelvin, UnitDegreeKelvin,
UnitMired,
UnitMilliBar, UnitMilliBar,
UnitBar, UnitBar,
UnitPascal, UnitPascal,

View File

@ -47,15 +47,15 @@
#include "devicepluginphilipshue.h" #include "devicepluginphilipshue.h"
#include "plugin/device.h"
#include "devicemanager.h" #include "devicemanager.h"
#include "plugin/device.h"
#include "types/param.h" #include "types/param.h"
#include "huebridgeconnection.h"
#include "plugininfo.h" #include "plugininfo.h"
#include <QDebug> #include <QDebug>
#include <QStringList> #include <QStringList>
#include <QColor> #include <QColor>
#include <QJsonDocument>
VendorId hueVendorId = VendorId(""); VendorId hueVendorId = VendorId("");
@ -74,26 +74,11 @@ StateTypeId hueReachableStateTypeId = StateTypeId("15794d26-fde8-4a61-8f83-d7830
DevicePluginPhilipsHue::DevicePluginPhilipsHue() DevicePluginPhilipsHue::DevicePluginPhilipsHue()
{ {
m_bridge = new HueBridgeConnection(this);
connect(m_bridge, &HueBridgeConnection::createUserFinished, this, &DevicePluginPhilipsHue::createUserFinished);
connect(m_bridge, &HueBridgeConnection::getFinished, this, &DevicePluginPhilipsHue::getFinished);
} }
DeviceManager::HardwareResources DevicePluginPhilipsHue::requiredHardware() const DeviceManager::HardwareResources DevicePluginPhilipsHue::requiredHardware() const
{ {
return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery; return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery | DeviceManager::HardwareResourceNetworkManager;
}
void DevicePluginPhilipsHue::startMonitoringAutoDevices()
{
// TODO: We could call the bridge to discover new light bulbs here maybe?
// Although we maybe want to think of a user triggered approach to do such things.
}
QList<ParamType> DevicePluginPhilipsHue::configurationDescription() const
{
QList<ParamType> params;
return params;
} }
DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params) DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params)
@ -106,208 +91,462 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceC
DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device) DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device)
{ {
Light *light = nullptr; // hue bridge
if (device->deviceClassId() == hueBridgeDeviceClassId) {
// unconfigured bridges (from pairing)
foreach (HueBridge *b, m_unconfiguredBridges) {
if (b->username() == device->paramValue("username").toString()) {
m_unconfiguredBridges.removeOne(b);
// Lets see if this a a newly added device... In which case its hue id number is not set, well, -1... // set data which was not known during discovery
if (device->paramValue("number").toInt() == -1) { device->setParamValue("name", b->name());
if (m_unconfiguredLights.count() > 0) { device->setParamValue("zigbee channel", b->zigbeeChannel());
light = m_unconfiguredLights.takeFirst(); device->setParamValue("api version", b->apiVersion());
device->setParamValue("number", light->id()); device->setParamValue("mac address", b->macAddress());
device->setParamValue("name", QString("Hue light %1").arg(light->id())); device->setStateValue(bridgeReachableStateTypeId, true);
} else { m_bridges.insert(b, device);
// this shouldn't ever happen
qWarning() << "Device not configured yet and no discovered devices around. This should not happen."; // now add the child lights from this bridge as auto device
return DeviceManager::DeviceSetupStatusFailure; 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("username", light->username()));
params.append(Param("bridge", device->id().toString()));
params.append(Param("host address", light->hostAddress().toString()));
params.append(Param("model id", light->modelId()));
params.append(Param("light id", light->lightId()));
descriptor.setParams(params);
descriptors.append(descriptor);
}
emit autoDevicesAppeared(hueLightDeviceClassId, descriptors);
return DeviceManager::DeviceSetupStatusSuccess;
}
} }
} else {
// In this case it most likely comes from the config. Just read all values from there... // loaded bridge
light = new Light(QHostAddress(device->paramValue("ip").toString()), device->paramValue("username").toString(), device->paramValue("number").toInt()); HueBridge *bridge = new HueBridge(device->paramValue("username").toString(),
QHostAddress(device->paramValue("host address").toString()));
bridge->setApiVersion(device->paramValue("api version").toString());
bridge->setMacAddress(device->paramValue("mac address").toString());
bridge->setName(device->paramValue("name").toString());
bridge->setZigbeeChannel(device->paramValue("zigbee channel").toInt());
m_bridges.insert(bridge, device);
return DeviceManager::DeviceSetupStatusSuccess;
} }
connect(light, &Light::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); // hue lights
light->refresh(); if (device->deviceClassId() == hueLightDeviceClassId) {
m_lights.insert(light, device); HueLight *hueLight = 0;
m_asyncSetups.insert(light, device);
// If we have more unconfigured lights around, lets add them as auto devices // check if this is a unconfigured light
QList<DeviceDescriptor> descriptorList; for (int i = 0; i < m_unconfiguredLights.count(); i++) {
while (!m_unconfiguredLights.isEmpty()) { if (m_unconfiguredLights.at(i)->username() == device->paramValue("username").toString()) {
Light *light = m_unconfiguredLights.takeFirst(); hueLight = m_unconfiguredLights.takeAt(i);
DeviceDescriptor descriptor(hueDeviceClassId, light->name()); break;
ParamList params; }
params.append(Param("name", light->name())); }
params.append(Param("number", light->id()));
params.append(Param("ip", light->ip().toString())); // check if this is a light from settings
params.append(Param("username", light->username())); if (hueLight == 0) {
descriptor.setParams(params); hueLight = new HueLight(device->paramValue("light id").toInt(),
descriptorList.append(descriptor); QHostAddress(device->paramValue("host address").toString()),
} device->paramValue("name").toString(),
if (!descriptorList.isEmpty()) { device->paramValue("username").toString(),
metaObject()->invokeMethod(this, "autoDevicesAppeared", Qt::QueuedConnection, Q_ARG(DeviceClassId, hueDeviceClassId), Q_ARG(QList<DeviceDescriptor>, descriptorList)); device->paramValue("model id").toString(),
DeviceId(device->paramValue("bridge").toString()),
this);
connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged);
}
device->setName(hueLight->name());
m_lights.insert(hueLight, device);
refreshLight(device);
} }
return DeviceManager::DeviceSetupStatusAsync; return DeviceManager::DeviceSetupStatusSuccess;
} }
void DevicePluginPhilipsHue::deviceRemoved(Device *device) void DevicePluginPhilipsHue::deviceRemoved(Device *device)
{ {
if (!m_lights.values().contains(device)) { Q_UNUSED(device)
return;
}
Light *light = m_lights.key(device);
m_lights.remove(light);
light->deleteLater();
} }
void DevicePluginPhilipsHue::upnpDiscoveryFinished(const QList<UpnpDeviceDescriptor> &upnpDeviceDescriptorList) void DevicePluginPhilipsHue::upnpDiscoveryFinished(const QList<UpnpDeviceDescriptor> &upnpDeviceDescriptorList)
{ {
foreach (const UpnpDeviceDescriptor &descriptor, upnpDeviceDescriptorList) {
qDebug() << descriptor;
}
QList<DeviceDescriptor> deviceDescriptors; QList<DeviceDescriptor> deviceDescriptors;
foreach (const UpnpDeviceDescriptor &upnpDevice, upnpDeviceDescriptorList) { foreach (const UpnpDeviceDescriptor &upnpDevice, upnpDeviceDescriptorList) {
if (upnpDevice.modelDescription().contains("Philips")) { if (upnpDevice.modelDescription().contains("Philips")) {
DeviceDescriptor descriptor(hueDeviceClassId, "Philips hue bridge", upnpDevice.hostAddress().toString()); DeviceDescriptor descriptor(hueBridgeDeviceClassId, "Philips Hue Bridge", upnpDevice.hostAddress().toString());
ParamList params; ParamList params;
params.append(Param("ip", upnpDevice.hostAddress().toString())); params.append(Param("name", QString()));
params.append(Param("host address", upnpDevice.hostAddress().toString()));
params.append(Param("username", "guh-" + QUuid::createUuid().toString().remove(QRegExp("[\\{\\}]*")).remove(QRegExp("\\-[0-9a-f\\-]*")))); params.append(Param("username", "guh-" + QUuid::createUuid().toString().remove(QRegExp("[\\{\\}]*")).remove(QRegExp("\\-[0-9a-f\\-]*"))));
params.append(Param("number", -1)); params.append(Param("mac address", QString()));
params.append(Param("api version", QString()));
params.append(Param("zigbee channel", -1));
descriptor.setParams(params); descriptor.setParams(params);
deviceDescriptors.append(descriptor); deviceDescriptors.append(descriptor);
} }
} }
emit devicesDiscovered(hueDeviceClassId, deviceDescriptors); emit devicesDiscovered(hueBridgeDeviceClassId, deviceDescriptors);
} }
DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList &params) DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList &params)
{ {
Q_UNUSED(deviceClassId) if (deviceClassId != hueBridgeDeviceClassId) {
Param ipParam;
foreach (const Param &param, params) {
if (param.name() == "ip") {
ipParam = param;
}
}
if (!ipParam.isValid()) {
qWarning() << "Missing parameter: ip";
return DeviceManager::DeviceSetupStatusFailure;
}
Param usernameParam;
foreach (const Param &param, params) {
if (param.name() == "username") {
usernameParam = param;
}
}
if (!usernameParam.isValid()) {
qWarning() << "Missing parameter: username";
return DeviceManager::DeviceSetupStatusFailure; return DeviceManager::DeviceSetupStatusFailure;
} }
int id = m_bridge->createUser(QHostAddress(ipParam.value().toString()), usernameParam.value().toString()); PairingInfo pairingInfo;
PairingInfo pi; pairingInfo.pairingTransactionId = pairingTransactionId;
pi.pairingTransactionId = pairingTransactionId; pairingInfo.host = QHostAddress(params.paramValue("host address").toString());
pi.ipParam = ipParam; pairingInfo.username = params.paramValue("username").toString();
pi.usernameParam = usernameParam;
m_pairings.insert(id, pi); QVariantMap createUserParams;
createUserParams.insert("devicetype", "guh");
createUserParams.insert("username", pairingInfo.username);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(createUserParams);
QNetworkRequest request(QUrl("http://" + pairingInfo.host.toString() + "/api"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply *reply = networkManagerPost(request, jsonDoc.toJson());
m_pairingRequests.insert(reply, pairingInfo);
return DeviceManager::DeviceSetupStatusAsync; return DeviceManager::DeviceSetupStatusAsync;
} }
void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply)
{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// create user finished
if (m_pairingRequests.keys().contains(reply)) {
PairingInfo pairingInfo = m_pairingRequests.take(reply);
// check HTTP status code
if (status != 200) {
qWarning() << "Request error:" << status << reply->errorString();
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
processPairingResponse(pairingInfo, data);
} else if (m_informationRequests.keys().contains(reply)) {
PairingInfo pairingInfo = m_informationRequests.take(reply);
// check HTTP status code
if (status != 200) {
qWarning() << "Request error:" << status << reply->errorString();
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
processInformationResponse(pairingInfo, data);
} else if (m_lightRefreshRequests.keys().contains(reply)) {
Device *device = m_lightRefreshRequests.take(reply);
// check HTTP status code
if (status != 200) {
qWarning() << "Refresh Hue Light request error:" << status << reply->errorString();
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
processLightRefreshResponse(device, data);
} else if (m_bridgeRefreshRequests.keys().contains(reply)) {
Device *device = m_bridgeRefreshRequests.take(reply);
// check HTTP status code
if (status != 200) {
qWarning() << "Refresh Hue Bridge request error:" << status << reply->errorString();
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
processBridgeRefreshResponse(device, data);
} else if (m_asyncActions.keys().contains(reply)) {
QPair<Device *, ActionId> actionInfo = m_asyncActions.take(reply);
// check HTTP status code
if (status != 200) {
qWarning() << "Refresh Hue Light request error:" << status << reply->errorString();
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
processActionResponse(actionInfo.first, actionInfo.second, data);
}
reply->deleteLater();
}
void DevicePluginPhilipsHue::guhTimer() void DevicePluginPhilipsHue::guhTimer()
{ {
foreach (Light *light, m_lights.keys()) { foreach (Device *device, m_bridges.values()) {
light->refresh(); refreshBridge(device);
} }
} }
DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const Action &action) DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const Action &action)
{ {
Light *light = m_lights.key(device); if (device->deviceClassId() == hueLightDeviceClassId) {
if (!light) { HueLight *light = m_lights.key(device);
return DeviceManager::DeviceErrorDeviceNotFound;
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() == hueColorActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = light->createSetColorRequest(action.param("color").value().value<QColor>());
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() == hueEffectActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = light->createSetEffectRequest(action.param("effect").value().toString());
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;
} else if (action.actionTypeId() == hueTemperatureActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = light->createSetTemperatureRequest(action.param("color temperature").value().toInt());
m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair<Device *, ActionId>(device, action.id()));
return DeviceManager::DeviceErrorAsync;
}
return DeviceManager::DeviceErrorActionTypeNotFound;
} }
if (!light->reachable()) { if (device->deviceClassId() == hueBridgeDeviceClassId) {
qWarning() << "Hue Bulb not reachable";
return DeviceManager::DeviceErrorHardwareNotAvailable;
}
if (action.actionTypeId() == hueColorActionTypeId) {
light->setColor(action.param("color").value().value<QColor>());
} else if (action.actionTypeId() == huePowerActionTypeId) {
light->setOn(action.param("power").value().toBool()); return DeviceManager::DeviceErrorActionTypeNotFound;
} else if (action.actionTypeId() == hueBrightnessActionTypeId) {
light->setBri(percentageToBrightness(action.param("brightness").value().toInt()));
} }
return DeviceManager::DeviceErrorNoError; return DeviceManager::DeviceErrorDeviceClassNotFound;
} }
void DevicePluginPhilipsHue::lightStateChanged()
void DevicePluginPhilipsHue::createUserFinished(int id, const QVariant &response)
{ {
qDebug() << "createuser response" << response; HueLight *light = static_cast<HueLight *>(sender());
PairingInfo pairingInfo = m_pairings.take(id); Device *device = m_lights.value(light);
if (response.toMap().contains("error")) { if (!device)
qDebug() << "Failed to pair Hue bridge:" << response.toMap().value("error").toMap().value("description"); return;
device->setStateValue(hueReachableStateTypeId, light->reachable());
device->setStateValue(hueColorStateTypeId, QVariant::fromValue(light->color()));
device->setStateValue(huePowerStateTypeId, light->power());
device->setStateValue(hueBrightnessStateTypeId, brightnessToPercentage(light->brightness()));
device->setStateValue(hueTemperatureStateTypeId, light->ct());
device->setStateValue(hueEffectStateTypeId, light->effect());
device->setStateValue(hueAlertStateTypeId, light->alert());
}
void DevicePluginPhilipsHue::refreshLight(Device *device)
{
HueLight *light = m_lights.key(device);
QNetworkRequest request(QUrl("http://" + light->hostAddress().toString() + "/api/" + light->username() + "/lights/" + QString::number(light->lightId())));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply *reply = networkManagerGet(request);
m_lightRefreshRequests.insert(reply, device);
}
void DevicePluginPhilipsHue::refreshBridge(Device *device)
{
HueBridge *bridge = m_bridges.key(device);
QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->username() + "/lights/"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply *reply = networkManagerGet(request);
m_bridgeRefreshRequests.insert(reply, device);
}
void DevicePluginPhilipsHue::processLightRefreshResponse(Device *device, const QByteArray &data)
{
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
// check JSON error
if (error.error != QJsonParseError::NoError) {
qWarning() << "Hue Bridge json error in response" << error.errorString();
return;
}
// check pairing error
if (data.contains("error")) {
qWarning() << "Failed to refresh Hue Light:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString();
return;
}
HueLight *hueLight = m_lights.key(device);
hueLight->setStates(jsonDoc.toVariant().toMap().value("state").toMap());
}
void DevicePluginPhilipsHue::processBridgeRefreshResponse(Device *device, const QByteArray &data)
{
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
// check JSON error
if (error.error != QJsonParseError::NoError) {
qWarning() << "Hue Bridge json error in response" << error.errorString();
return;
}
// check pairing error
if (data.contains("error")) {
qWarning() << "Failed to refresh Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString();
return;
}
QVariantMap lightsMap = jsonDoc.toVariant().toMap();
foreach (const QString &lightId, lightsMap.keys()) {
QVariantMap lightMap = lightsMap.value(lightId).toMap();
// get the light of this bridge
foreach (HueLight *light, m_lights.keys()) {
if (light->lightId() == lightId.toInt() && light->bridgeId() == device->id()) {
light->setStates(lightMap.value("state").toMap());
}
}
}
}
void DevicePluginPhilipsHue::processPairingResponse(const DevicePluginPhilipsHue::PairingInfo &pairingInfo, const QByteArray &data)
{
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
// check JSON error
if (error.error != QJsonParseError::NoError) {
qWarning() << "Hue Bridge json error in response" << error.errorString();
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure); emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure);
return; return;
} }
// Paired successfully, check how many lightbulbs there are // check pairing error
int getLightsId = m_bridge->get(QHostAddress(pairingInfo.ipParam.value().toString()), pairingInfo.usernameParam.value().toString(), "lights", this, "getLightsFinished"); if (data.contains("error")) {
m_pairings.insert(getLightsId, pairingInfo); qWarning() << "Failed to pair Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString();
}
void DevicePluginPhilipsHue::getLightsFinished(int id, const QVariant &params)
{
qDebug() << "getlightsfinished" << params;
PairingInfo pairingInfo = m_pairings.take(id);
if (params.toMap().count() == 0) {
qWarning() << "No light bulbs found on this hue bridge... Cannot proceed with pairing.";
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure); emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure);
return; return;
} }
// Store a list of all known Lights // Paired successfully, check bridge/light information
foreach (const QString &lightId, params.toMap().keys()) { QNetworkRequest request(QUrl("http://" + pairingInfo.host.toString() + "/api/" + pairingInfo.username + ""));
Light *light = new Light(QHostAddress(pairingInfo.ipParam.value().toString()), pairingInfo.usernameParam.value().toString(), lightId.toInt(), this); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
m_unconfiguredLights.insert(lightId.toInt(), light); QNetworkReply *reply = networkManagerGet(request);
m_informationRequests.insert(reply, pairingInfo);
}
void DevicePluginPhilipsHue::processInformationResponse(const DevicePluginPhilipsHue::PairingInfo &pairingInfo, const QByteArray &data)
{
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
// check JSON error
if (error.error != QJsonParseError::NoError) {
qWarning() << "Hue Bridge json error in response" << error.errorString();
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure);
return;
}
QVariantMap response = jsonDoc.toVariant().toMap();
// check json error
if (response.contains("error")) {
qWarning() << "Failed to get information from Hue Bridge:" << response.value("error").toMap().value("description").toString();
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusFailure);
return;
}
// create Bridge
HueBridge *bridge = new HueBridge(pairingInfo.username, pairingInfo.host);
bridge->setApiVersion(response.value("config").toMap().value("apiversion").toString());
bridge->setMacAddress(response.value("config").toMap().value("mac").toString());
bridge->setName(response.value("config").toMap().value("name").toString());
bridge->setZigbeeChannel(response.value("config").toMap().value("zigbeechannel").toInt());
m_unconfiguredBridges.append(bridge);
// create Lights
QVariantMap lightsMap = response.value("lights").toMap();
foreach (QString lightId, lightsMap.keys()) {
QVariantMap lightMap = lightsMap.value(lightId).toMap();
HueLight *hueLight = new HueLight(lightId.toInt(),
bridge->hostAddress(),
lightMap.value("name").toString(),
pairingInfo.username,
lightMap.value("modelid").toString(),
DeviceId(),
this);
hueLight->setStates(lightMap.value("state").toMap());
bridge->addLight(hueLight);
m_unconfiguredLights.append(hueLight);
connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged);
} }
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess); emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess);
} }
void DevicePluginPhilipsHue::getFinished(int id, const QVariant &params) void DevicePluginPhilipsHue::processActionResponse(Device *device, const ActionId actionId, const QByteArray &data)
{ {
qDebug() << "got lights" << params << id; QJsonParseError error;
} QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
void DevicePluginPhilipsHue::lightStateChanged() // check JSON error
{ if (error.error != QJsonParseError::NoError) {
Light *light = static_cast<Light*>(sender()); qWarning() << "Hue Bridge json error in response" << error.errorString();
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable);
Device *device;
if (m_asyncSetups.contains(light)) {
device = m_asyncSetups.take(light);
device->setName(light->name());
device->setParamValue("name", light->name());
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
} else {
device = m_lights.value(light);
}
if (!device) {
return; return;
} }
device->setStateValue(hueReachableStateTypeId, light->reachable());
device->setStateValue(hueColorStateTypeId, QVariant::fromValue(light->color())); // check pairing error
device->setStateValue(huePowerStateTypeId, light->on()); if (data.contains("error")) {
device->setStateValue(hueBrightnessStateTypeId, brightnessToPercentage(light->bri())); qWarning() << "Failed to execute Hue action:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString();
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable);
return;
}
m_lights.key(device)->processActionResponse(jsonDoc.toVariant().toList());
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
} }
int DevicePluginPhilipsHue::brightnessToPercentage(int brightness) int DevicePluginPhilipsHue::brightnessToPercentage(int brightness)

View File

@ -1,6 +1,7 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> * * Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* * * *
* This file is part of guh. * * This file is part of guh. *
* * * *
@ -22,8 +23,8 @@
#define DEVICEPLUGINPHILIPSHUE_H #define DEVICEPLUGINPHILIPSHUE_H
#include "plugin/deviceplugin.h" #include "plugin/deviceplugin.h"
#include "huebridgeconnection.h" #include "huebridge.h"
#include "light.h" #include "huelight.h"
class QNetworkReply; class QNetworkReply;
@ -38,28 +39,21 @@ public:
explicit DevicePluginPhilipsHue(); explicit DevicePluginPhilipsHue();
DeviceManager::HardwareResources requiredHardware() const override; DeviceManager::HardwareResources requiredHardware() const override;
void startMonitoringAutoDevices() override;
QList<ParamType> configurationDescription() const override;
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params) override;
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params) override;
void deviceRemoved(Device *device) 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) override; DeviceManager::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList &params) override;
void networkManagerReplyReady(QNetworkReply *reply) override;
void guhTimer() override; void guhTimer() override;
public slots: public slots:
DeviceManager::DeviceError executeAction(Device *device, const Action &action); DeviceManager::DeviceError executeAction(Device *device, const Action &action);
private slots: private slots:
void createUserFinished(int id, const QVariant &params);
void getLightsFinished(int id, const QVariant &params);
void getFinished(int id, const QVariant &params);
void lightStateChanged(); void lightStateChanged();
private: private:
@ -67,17 +61,31 @@ private:
class PairingInfo { class PairingInfo {
public: public:
PairingTransactionId pairingTransactionId; PairingTransactionId pairingTransactionId;
Param ipParam; QHostAddress host;
Param usernameParam; QString username;
}; };
QHash<int, PairingInfo> m_pairings; QHash<QNetworkReply *, PairingInfo> m_pairingRequests;
HueBridgeConnection *m_bridge; QHash<QNetworkReply *, PairingInfo> m_informationRequests;
QList<Light*> m_unconfiguredLights; QList<HueBridge *> m_unconfiguredBridges;
QHash<Light*, Device*> m_lights; QList<HueLight *> m_unconfiguredLights;
QHash<Light*, Device*> m_asyncSetups; QHash<QNetworkReply *, Device *> m_lightRefreshRequests;
QHash<QNetworkReply *, Device *> m_bridgeRefreshRequests;
QHash<QNetworkReply *, QPair<Device *, ActionId> > m_asyncActions;
QHash<HueBridge*, Device*> m_bridges;
QHash<HueLight*, Device*> m_lights;
void refreshLight(Device *device);
void refreshBridge(Device *device);
void processLightRefreshResponse(Device *device, const QByteArray &data);
void processBridgeRefreshResponse(Device *device, const QByteArray &data);
void processPairingResponse(const PairingInfo &pairingInfo, const QByteArray &data);
void processInformationResponse(const PairingInfo &pairingInfo, const QByteArray &data);
void processActionResponse(Device *device, const ActionId actionId, const QByteArray &data);
int brightnessToPercentage(int brightness); int brightnessToPercentage(int brightness);
int percentageToBrightness(int percentage); int percentageToBrightness(int percentage);

View File

@ -7,12 +7,12 @@
"name": "Philips", "name": "Philips",
"deviceClasses": [ "deviceClasses": [
{ {
"deviceClassId": "d8f4c397-e05e-47c1-8917-8e72d4d0d47c", "deviceClassId": "642aa4c7-19aa-45ed-ba06-aa1ae6c9edf7",
"idName": "hue", "idName": "hueBridge",
"name": "Hue", "name": "Hue Bridge",
"createMethods": ["discovery", "auto"], "createMethods": ["discovery"],
"setupMethod": "pushButton", "setupMethod": "pushButton",
"pairingInfo": "Please press the button on the Hue bridge and then press OK", "pairingInfo": "Please press the button on the Hue Bridge before you continue",
"paramTypes": [ "paramTypes": [
{ {
"name": "name", "name": "name",
@ -20,11 +20,71 @@
"inputType": "TextLine" "inputType": "TextLine"
}, },
{ {
"name": "ip", "name": "username",
"type" : "QString",
"inputType": "TextLine",
"readOnly": true
},
{
"name": "host address",
"type" : "QString", "type" : "QString",
"inputType": "IPv4Address", "inputType": "IPv4Address",
"readOnly": true "readOnly": true
}, },
{
"name": "mac address",
"type" : "QString",
"inputType": "MacAddress",
"readOnly": true
},
{
"name": "api version",
"type" : "QString",
"readOnly": true
},
{
"name": "zigbee channel",
"type" : "int",
"readOnly": true
}
],
"stateTypes": [
{
"id": "15794d26-fde8-4a61-8f83-d7830534975f",
"idName": "bridgeReachable",
"name": "reachable",
"type": "bool"
}
],
"actionTypes": [
{
"id": "001476ce-2f17-475f-939f-d4234751ef35",
"idName": "searchLamps",
"name": "search new lamps"
}
]
},
{
"deviceClassId": "0edba26c-96ab-44fb-a6a2-c0574d19630e",
"idName": "hueLight",
"name": "Hue Light",
"createMethods": ["auto"],
"paramTypes": [
{
"name": "name",
"type" : "QString",
"inputType": "TextLine"
},
{
"name": "bridge",
"type" : "QString",
"readOnly": true
},
{
"name": "model id",
"type" : "QString",
"readOnly": true
},
{ {
"name": "username", "name": "username",
"type" : "QString", "type" : "QString",
@ -32,18 +92,43 @@
"readOnly": true "readOnly": true
}, },
{ {
"name": "lightId", "name": "host address",
"type" : "QString",
"inputType": "IPv4Address",
"readOnly": true
},
{
"name": "light id",
"type" : "int", "type" : "int",
"readOnly": true "readOnly": true
} }
], ],
"stateTypes": [ "stateTypes": [
{ {
"id": "15794d26-fde8-4a61-8f83-d7830534975f", "id": "19bb8d10-1b28-4ba3-99b7-a634138dcfde",
"idName": "hueReachable", "idName": "hueReachable",
"name": "reachable", "name": "reachable",
"type": "bool" "type": "bool"
}, },
{
"id": "90aaffe5-6a76-47d2-a14a-550f60390245",
"idName": "huePower",
"name": "power",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "c0f4206f-f219-4f06-93c4-4ca515a56f79",
"idName": "hueTemperature",
"name": "color temperature",
"type": "int",
"unit": "Mired",
"writable": true,
"defaultValue": 170,
"minValue": 154,
"maxValue": 500
},
{ {
"id": "d25423e7-b924-4b20-80b6-77eecc65d089", "id": "d25423e7-b924-4b20-80b6-77eecc65d089",
"idName": "hueColor", "idName": "hueColor",
@ -53,24 +138,41 @@
"writable": true "writable": true
}, },
{
"id": "90aaffe5-6a76-47d2-a14a-550f60390245",
"idName": "huePower",
"name": "power",
"type": "bool",
"defaultValue": false,
"writable": true
},
{ {
"id": "90e91f64-a208-468c-a5a2-7f47e08859e2", "id": "90e91f64-a208-468c-a5a2-7f47e08859e2",
"idName": "hueBrightness", "idName": "hueBrightness",
"name": "brightness", "name": "brightness",
"type": "int", "type": "int",
"unit": "Percentage",
"minValue": 0, "minValue": 0,
"maxValue": 100, "maxValue": 100,
"unit": "Percentage",
"defaultValue": 0, "defaultValue": 0,
"writable": true "writable": true
},
{
"id": "0b7cdd8d-4db8-4183-abe2-f3c01d1c9afc",
"idName": "hueEffect",
"name": "effect",
"type": "QString",
"defaultValue": "none",
"allowedValues": [
"none",
"color loop"
],
"writable": true
},
{
"id": "acd09e71-3305-451c-910a-bc2d6e1d5144",
"idName": "hueAlert",
"name": "alert",
"type": "QString",
"defaultValue": "none",
"allowedValues": [
"none",
"flash",
"flash 30 seconds"
],
"writable": true
} }
] ]
} }

View File

@ -0,0 +1,103 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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 "huebridge.h"
HueBridge::HueBridge(QString username, QHostAddress hostAddress, QObject *parent) :
QObject(parent),
m_username(username),
m_hostAddress(hostAddress),
m_name(QString()),
m_macAddress(QString()),
m_apiVersion(QString()),
m_zigbeeChannel(-1)
{
}
QString HueBridge::name() const
{
return m_name;
}
void HueBridge::setName(const QString &name)
{
m_name = name;
}
QString HueBridge::username() const
{
return m_username;
}
void HueBridge::setUsername(const QString &username)
{
m_username = username;
}
QHostAddress HueBridge::hostAddress() const
{
return m_hostAddress;
}
void HueBridge::setHostAddress(const QHostAddress &hostAddress)
{
m_hostAddress = hostAddress;
}
QString HueBridge::macAddress() const
{
return m_macAddress;
}
void HueBridge::setMacAddress(const QString &macAddress)
{
m_macAddress = macAddress;
}
QString HueBridge::apiVersion() const
{
return m_apiVersion;
}
void HueBridge::setApiVersion(const QString &apiVersion)
{
m_apiVersion = apiVersion;
}
int HueBridge::zigbeeChannel() const
{
return m_zigbeeChannel;
}
void HueBridge::setZigbeeChannel(const int &zigbeeChannel)
{
m_zigbeeChannel = zigbeeChannel;
}
QList<HueLight *> HueBridge::lights() const
{
return m_lights;
}
void HueBridge::addLight(HueLight *light)
{
m_lights.append(light);
}

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> * * Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* * * *
* This file is part of guh. * * This file is part of guh. *
* * * *
@ -18,46 +18,55 @@
* * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef HUEBRIDGECONNECTION_H #ifndef HUEBRIDGE_H
#define HUEBRIDGECONNECTION_H #define HUEBRIDGE_H
#include <QObject> #include <QObject>
#include <QHostAddress> #include <QHostAddress>
#include <QNetworkAccessManager>
#include <QPointer>
class Caller #include "huelight.h"
{
public:
QPointer<QObject> obj;
QString method;
int id;
};
class HueBridgeConnection : public QObject class HueBridge : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit HueBridgeConnection(QObject *parent = 0); explicit HueBridge(QString username, QHostAddress hostAddress, QObject *parent = 0);
int createUser(const QHostAddress &address, const QString &username); QString name() const;
void setName(const QString &name);
int get(const QHostAddress &address, const QString &username, const QString &path, QObject *caller, const QString &methodName); QString username() const;
int put(const QHostAddress &address, const QString &username, const QString &path, const QVariantMap &data, QObject *caller, const QString &methodName); void setUsername(const QString &username);
private slots: QHostAddress hostAddress() const;
void slotCreateUserFinished(); void setHostAddress(const QHostAddress &hostAddress);
void slotGetFinished();
signals: QString macAddress() const;
void createUserFinished(int id, const QVariantMap &map); void setMacAddress(const QString &macAddress);
void getFinished(int id, const QVariantMap &map);
QString apiVersion() const;
void setApiVersion(const QString &apiVersion);
int zigbeeChannel() const;
void setZigbeeChannel(const int &zigbeeChannel);
QList<HueLight *> lights() const;
void addLight(HueLight *light);
private: private:
QNetworkAccessManager *m_nam; QString m_username;
int m_requestCounter; QHostAddress m_hostAddress;
QHash<QNetworkReply*, int> m_createUserMap; QString m_name;
QHash<QNetworkReply*, Caller> m_requestMap; QString m_macAddress;
QString m_apiVersion;
int m_zigbeeChannel;
QList<HueLight *> m_lights;
signals:
public slots:
}; };
#endif // HUEBRIDGECONNECTION_H #endif // HUEBRIDGE_H

View File

@ -1,148 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2013 Michael Zanetti <michael_zanetti@gmx.net> *
* *
* 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 "huebridgeconnection.h"
#include <QJsonDocument>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
HueBridgeConnection::HueBridgeConnection(QObject *parent) :
QObject(parent)
{
m_nam = new QNetworkAccessManager(this);
}
int HueBridgeConnection::createUser(const QHostAddress &address, const QString &username)
{
QVariantMap createUserParams;
createUserParams.insert("devicetype", "guh");
createUserParams.insert("username", username);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(createUserParams);
QByteArray data = jsonDoc.toJson();
QNetworkRequest request(QUrl("http://" + address.toString() + "/api"));
QNetworkReply *reply = m_nam->post(request, data);
connect(reply, &QNetworkReply::finished, this, &HueBridgeConnection::slotCreateUserFinished);
m_createUserMap.insert(reply, m_requestCounter);
return m_requestCounter++;
}
int HueBridgeConnection::get(const QHostAddress &address, const QString &username, const QString &path, QObject *caller, const QString &methodName)
{
QString baseUrl = "http://" + address.toString() + "/api/" + username + "/";
QUrl url(baseUrl + path);
QNetworkRequest request;
request.setUrl(url);
QNetworkReply *reply = m_nam->get(request);
connect(reply, &QNetworkReply::finished, this, &HueBridgeConnection::slotGetFinished);
Caller c;
c.obj = caller;
c.method = methodName;
c.id = m_requestCounter;
m_requestMap.insert(reply, c);
return m_requestCounter++;
}
int HueBridgeConnection::put(const QHostAddress &address, const QString &username, const QString &path, const QVariantMap &data, QObject *caller, const QString &methodName)
{
QString baseUrl = "http://" + address.toString() + "/api/" + username + "/";
QUrl url(baseUrl + path);
QNetworkRequest request;
request.setUrl(url);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(data);
QByteArray jsonData = jsonDoc.toJson();
//qDebug() << "putting" << url << jsonData;
QNetworkReply *reply = m_nam->put(request, jsonData);
connect(reply, &QNetworkReply::finished, this, &HueBridgeConnection::slotGetFinished);
Caller c;
c.obj = caller;
c.method = methodName;
c.id = m_requestCounter;
m_requestMap.insert(reply, c);
return m_requestCounter++;
}
void HueBridgeConnection::slotCreateUserFinished()
{
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
QByteArray data = reply->readAll();
int id = m_createUserMap.take(reply);
reply->deleteLater();
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
qDebug() << jsonDoc.toJson();
if (error.error != QJsonParseError::NoError) {
QVariantMap params;
QVariantMap errorMap;
errorMap.insert("description", "Failed to parse the bridge's response:" + error.errorString());
params.insert("error", errorMap);
emit createUserFinished(id, params);
return;
}
QVariantMap response = jsonDoc.toVariant().toList().first().toMap();
emit createUserFinished(id, response);
}
void HueBridgeConnection::slotGetFinished()
{
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
QByteArray data = reply->readAll();
Caller c = m_requestMap.take(reply);
reply->deleteLater();
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
QVariantMap params;
QVariantMap errorMap;
errorMap.insert("description", "Failed to parse the bridge's response:" + error.errorString());
params.insert("error", errorMap);
emit createUserFinished(c.id, params);
return;
}
if (jsonDoc.toJson().contains("error")){
qDebug() << jsonDoc.toJson();
}
QVariant response = jsonDoc.toVariant();
emit getFinished(c.id, response.toMap());
if (c.obj) {
metaObject()->invokeMethod(c.obj.data(), c.method.toLatin1().data(), Q_ARG(int, c.id), Q_ARG(QVariant, response));
}
}

View File

@ -0,0 +1,397 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
* 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 "huelight.h"
HueLight::HueLight(const int &lightId, const QHostAddress &hostAddress, const QString &name, const QString &username, const QString &modelId, const DeviceId &bridgeId, QObject *parent) :
QObject(parent),
m_lightId(lightId),
m_hostAddress(hostAddress),
m_name(name),
m_username(username),
m_modelId(modelId),
m_bridgeId(bridgeId)
{
}
int HueLight::lightId() const
{
return m_lightId;
}
void HueLight::setLightId(const int &lightId)
{
m_lightId = lightId;
}
DeviceId HueLight::bridgeId() const
{
return m_bridgeId;
}
void HueLight::setBridgeId(const DeviceId &bridgeDeviceId)
{
m_bridgeId = bridgeDeviceId;
}
QHostAddress HueLight::hostAddress() const
{
return m_hostAddress;
}
void HueLight::setHostAddress(const QHostAddress hostAddress)
{
m_hostAddress = hostAddress;
}
QString HueLight::name() const
{
return m_name;
}
void HueLight::setName(const QString &name)
{
m_name = name;
}
QString HueLight::username() const
{
return m_username;
}
void HueLight::setUsername(const QString &username)
{
m_username = username;
}
QString HueLight::modelId() const
{
return m_modelId;
}
void HueLight::setModelId(const QString &modelId)
{
m_modelId = modelId;
}
QString HueLight::type() const
{
return m_type;
}
void HueLight::setType(const QString &type)
{
m_type = type;
}
QString HueLight::softwareVersion() const
{
return m_softwareVersion;
}
void HueLight::setSoftwareVersion(const QString &softwareVersion)
{
m_softwareVersion = softwareVersion;
}
bool HueLight::power() const
{
return m_power;
}
void HueLight::setPower(const bool &power)
{
m_power = power;
}
bool HueLight::reachable() const
{
return m_reachable;
}
void HueLight::setReachable(const bool &reachable)
{
m_reachable = reachable;
}
quint8 HueLight::brightness() const
{
return m_brightness;
}
void HueLight::setBrigtness(const quint8 brightness)
{
m_brightness = brightness;
}
quint16 HueLight::hue() const
{
return m_hue;
}
void HueLight::setHue(const quint16 hue)
{
m_hue = hue;
}
quint8 HueLight::sat() const
{
return m_sat;
}
void HueLight::setSat(const quint8 sat)
{
m_sat = sat;
}
QColor HueLight::color() const
{
return QColor::fromHsv(m_hue * 360 / 65535, m_sat, 255);
}
QPointF HueLight::xy() const
{
return m_xy;
}
void HueLight::setXy(const QPointF &xy)
{
m_xy = xy;
}
quint16 HueLight::ct() const
{
return m_ct;
}
void HueLight::setCt(const quint16 &ct)
{
m_ct = ct;
}
QString HueLight::alert() const
{
return m_alert;
}
void HueLight::setAlert(const QString &alert)
{
m_alert = alert;
}
QString HueLight::effect() const
{
return m_effect;
}
void HueLight::setEffect(const QString &effect)
{
m_effect = effect;
}
HueLight::ColorMode HueLight::colorMode() const
{
return m_colorMode;
}
void HueLight::setColorMode(const HueLight::ColorMode &colorMode)
{
m_colorMode = colorMode;
}
void HueLight::setStates(const QVariantMap &statesMap)
{
// color mode
if (statesMap.value("colormode").toString() == "hs") {
setColorMode(ColorModeHS);
} else if (statesMap.value("colormode").toString() == "ct") {
setColorMode(ColorModeCT);
} else if (statesMap.value("colormode").toString() == "xy") {
setColorMode(ColorModeXY);
}
// effect (none, colorloop)
if (statesMap.value("effect").toString() == "none") {
setEffect("none");
} else if (statesMap.value("effect").toString() == "colorloop") {
setEffect("color loop");
}
// alert (none, select, lselect)
setAlert(statesMap.value("alert").toString());
setBrigtness(statesMap.value("bri").toInt());
setCt(statesMap.value("ct").toInt());
setPower(statesMap.value("on").toBool());
setReachable(statesMap.value("reachable").toBool());
setSat(statesMap.value("sat").toInt());
setHue(statesMap.value("hue").toInt());
setXy(QPointF(statesMap.value("xy").toList().first().toFloat(),statesMap.value("xy").toList().last().toFloat()));
emit stateChanged();
}
void HueLight::processActionResponse(const QVariantList &responseList)
{
foreach (const QVariant &resultVariant, responseList) {
QVariantMap result = resultVariant.toMap();
if (result.contains("success")) {
QVariantMap successMap = result.value("success").toMap();
if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/on")) {
m_power = successMap.value("/lights/" + QString::number(m_lightId) + "/state/on").toBool();
}
if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/hue")) {
m_hue = successMap.value("/lights/" + QString::number(m_lightId) + "/state/hue").toInt();
m_colorMode = ColorModeHS;
}
if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/bri")) {
m_brightness = successMap.value("/lights/" + QString::number(m_lightId) + "/state/bri").toInt();
}
if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/sat")) {
m_sat = successMap.value("/lights/" + QString::number(m_lightId) + "/state/sat").toInt();
m_colorMode = ColorModeHS;
}
if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/xy")) {
m_xy = successMap.value("/lights/" + QString::number(m_lightId) + "/state/xy").toPoint();
m_colorMode = ColorModeXY;
}
if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/ct")) {
m_ct = successMap.value("/lights/" + QString::number(m_lightId) + "/state/ct").toInt();
m_colorMode = ColorModeCT;
}
if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/effect")) {
QString effect = successMap.value("/lights/" + QString::number(m_lightId) + "/state/effect").toString();
if (effect == "none") {
setEffect("none");
} else if (effect == "colorloop") {
setEffect("color loop");
}
}
if (successMap.contains("/lights/" + QString::number(m_lightId) + "/state/alert")) {
QString alert = successMap.value("/lights/" + QString::number(m_lightId) + "/state/alert").toString();
if (alert == "none") {
m_alert = "none";
} else if (alert == "select") {
m_alert = "flash";
} else if (alert == "lselect") {
m_alert = "flash 30 seconds";
}
}
}
}
emit stateChanged();
}
QPair<QNetworkRequest, QByteArray> HueLight::createSetPowerRequest(const bool &power)
{
QVariantMap requestMap;
requestMap.insert("on", power);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap);
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() +
"/lights/" + QString::number(lightId()) + "/state"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
return QPair<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
QPair<QNetworkRequest, QByteArray> HueLight::createSetColorRequest(const QColor &color)
{
QVariantMap requestMap;
requestMap.insert("hue", color.hue() * 65535 / 360);
requestMap.insert("sat", color.saturation());
requestMap.insert("on", true);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap);
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() +
"/lights/" + QString::number(lightId()) + "/state"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
return QPair<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
QPair<QNetworkRequest, QByteArray> HueLight::createSetBrightnessRequest(const int &brightness)
{
QVariantMap requestMap;
requestMap.insert("bri", brightness);
if (brightness == 0) {
requestMap.insert("on", false);
} else {
requestMap.insert("on", true);
}
QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap);
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() +
"/lights/" + QString::number(lightId()) + "/state"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
return QPair<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
QPair<QNetworkRequest, QByteArray> HueLight::createSetEffectRequest(const QString &effect)
{
QVariantMap requestMap;
if (effect == "none") {
requestMap.insert("effect", "none");
} else if (effect == "color loop") {
requestMap.insert("effect", "colorloop");
requestMap.insert("on", true);
}
QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap);
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() +
"/lights/" + QString::number(lightId()) + "/state"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
return QPair<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
QPair<QNetworkRequest, QByteArray> HueLight::createSetTemperatureRequest(const int &colorTemp)
{
QVariantMap requestMap;
requestMap.insert("ct", colorTemp);
requestMap.insert("on", true);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap);
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() +
"/lights/" + QString::number(lightId()) + "/state"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
return QPair<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}
QPair<QNetworkRequest, QByteArray> HueLight::createFlashRequest(const QString &alert)
{
QVariantMap requestMap;
if (alert == "none") {
requestMap.insert("alert", "none");
} else if (alert == "flash") {
requestMap.insert("alert", "select");
} else if (alert == "flash 30 seconds") {
requestMap.insert("alert", "lselect");
}
QJsonDocument jsonDoc = QJsonDocument::fromVariant(requestMap);
QNetworkRequest request(QUrl("http://" + hostAddress().toString() + "/api/" + username() +
"/lights/" + QString::number(lightId()) + "/state"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
return QPair<QNetworkRequest, QByteArray>(request, jsonDoc.toJson());
}

View File

@ -1,6 +1,7 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* Copyright (C) 2013 Michael Zanetti <michael_zanetti@gmx.net> * * Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* * * *
* This file is part of guh. * * This file is part of guh. *
* * * *
@ -18,122 +19,128 @@
* * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LIGHT_H #ifndef HUELIGHT_H
#define LIGHT_H #define HUELIGHT_H
#include <QObject> #include <QObject>
#include <QPointF> #include <QDebug>
#include <QColor> #include <QColor>
#include <QPoint>
#include <QHostAddress> #include <QHostAddress>
#include <QTimer> #include <QNetworkRequest>
#include <QJsonDocument>
class HueBridgeConnection; #include "typeutils.h"
#include "types/action.h"
class Light: public QObject class HueLight : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
enum ColorMode { enum ColorMode {
ColorModeHS, ColorModeHS,
ColorModeXY, ColorModeXY,
ColorModeCT ColorModeCT
}; };
Light(const QHostAddress &ip, const QString &username, int id, QObject *parent = 0); explicit HueLight(const int &lightId, const QHostAddress &hostAddress, const QString &name, const QString &username, const QString &modelId, const DeviceId &bridgeId, QObject *parent = 0);
QHostAddress ip() const; int lightId() const;
QString username() const; void setLightId(const int &lightId);
int id() const; DeviceId bridgeId() const;
void setBridgeId(const DeviceId &bridgeDeviceId);
QHostAddress hostAddress() const;
void setHostAddress(const QHostAddress hostAddress);
QString name() const; QString name() const;
void setName(const QString &name); void setName(const QString &name);
QString username() const;
void setUsername(const QString &username);
QString modelId() const; QString modelId() const;
void setModelId(const QString &modelId); void setModelId(const QString &modelId);
QString type() const; QString type() const;
void setType(const QString &type); void setType(const QString &type);
QString swversion() const; QString softwareVersion() const;
void setSwversion(const QString &swversion); void setSoftwareVersion(const QString &softwareVersion);
bool power() const;
void setPower(const bool &power);
// LightInterface implementation
bool on() const;
quint8 bri() const;
quint16 hue() const;
quint8 sat() const;
QColor color() const;
QPointF xy() const;
quint16 ct() const;
QString alert() const;
QString effect() const;
ColorMode colorMode() const;
bool reachable() const; bool reachable() const;
void setReachable(const bool &reachable);
quint8 brightness() const;
void setBrigtness(const quint8 brightness);
public slots: quint16 hue() const;
void refresh(); void setHue(const quint16 hue);
void setOn(bool on); quint8 sat() const;
void setBri(quint8 bri); void setSat(const quint8 sat);
void setHue(quint16 hue);
void setSat(quint8 sat); QColor color() const;
void setColor(const QColor &color);
QPointF xy() const;
void setXy(const QPointF &xy); void setXy(const QPointF &xy);
void setCt(quint16 ct);
quint16 ct() const;
void setCt(const quint16 &ct);
QString alert() const;
void setAlert(const QString &alert); void setAlert(const QString &alert);
QString effect() const;
void setEffect(const QString &effect); void setEffect(const QString &effect);
signals: ColorMode colorMode() const;
void stateChanged(); void setColorMode(const ColorMode &colorMode);
private slots: // update states
void responseReceived(int id, const QVariant &response); void setStates(const QVariantMap &statesMap);
void setDescriptionFinished(int id, const QVariant &response); void processActionResponse(const QVariantList &responseList);
void setStateFinished(int id, const QVariant &response);
// create action requests
QPair<QNetworkRequest, QByteArray> createSetPowerRequest(const bool &power);
QPair<QNetworkRequest, QByteArray> createSetColorRequest(const QColor &color);
QPair<QNetworkRequest, QByteArray> createSetBrightnessRequest(const int &brightness);
QPair<QNetworkRequest, QByteArray> createSetEffectRequest(const QString &effect);
QPair<QNetworkRequest, QByteArray> createSetTemperatureRequest(const int &colorTemp);
QPair<QNetworkRequest, QByteArray> createFlashRequest(const QString &alert);
private: private:
void setReachable(bool reachable); int m_lightId;
QHostAddress m_hostAddress;
HueBridgeConnection *m_bridge;
QHostAddress m_ip;
QString m_username;
int m_id;
QString m_name; QString m_name;
QString m_username;
QString m_modelId; QString m_modelId;
DeviceId m_bridgeId;
QString m_type; QString m_type;
QString m_swversion; QString m_softwareVersion;
bool m_on; bool m_power;
quint8 m_bri; bool m_reachable;
quint8 m_brightness;
quint16 m_hue; quint16 m_hue;
quint8 m_sat; quint8 m_sat;
QPointF m_xy; QPointF m_xy;
quint16 m_ct; quint16 m_ct;
QString m_alert; QString m_alert;
QString m_effect; QString m_effect;
ColorMode m_colormode; ColorMode m_colorMode;
bool m_reachable;
int m_busyStateChangeId; signals:
bool m_hueDirty; void stateChanged();
quint16 m_dirtyHue;
bool m_satDirty; public slots:
quint8 m_dirtySat;
bool m_briDirty;
quint8 m_dirtyBri;
bool m_ctDirty;
quint16 m_dirtyCt;
bool m_xyDirty;
QPointF m_dirtyXy;
// FIXME: This is needed as sometimes we don't get a reply from the bridge
// Can't use guhtimer right now as that triggers in intervals that aren't
// related to our sending. Perhaps we should create a guhtimeout thing?
QTimer m_busyTimeout;
}; };
#endif #endif // HUELIGHT_H

View File

@ -1,421 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2013 Michael Zanetti <michael_zanetti@gmx.net> *
* *
* 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 "light.h"
#include "huebridgeconnection.h"
#include <QColor>
#include <QDebug>
#include <QGenericMatrix>
Light::Light(const QHostAddress &ip, const QString &username, int id, QObject *parent):
QObject(parent),
m_bridge(new HueBridgeConnection(this)),
m_ip(ip),
m_username(username),
m_id(id),
m_on(false),
m_busyStateChangeId(-1),
m_hueDirty(false),
m_satDirty(false),
m_briDirty(false),
m_ctDirty(false),
m_xyDirty(false)
{
m_busyTimeout.setSingleShot(true);
m_busyTimeout.setInterval(2000);
connect(&m_busyTimeout, &QTimer::timeout, [this]{ setStateFinished(m_busyStateChangeId, QVariant()); });
}
QHostAddress Light::ip() const
{
return m_ip;
}
QString Light::username() const
{
return m_username;
}
int Light::id() const
{
return m_id;
}
QString Light::name() const
{
return m_name;
}
void Light::setName(const QString &name)
{
if (m_name != name) {
QVariantMap params;
params.insert("name", name);
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id), params, this, "setDescriptionFinished");
}
}
QString Light::modelId() const
{
return m_modelId;
}
void Light::setModelId(const QString &modelId)
{
if (m_modelId != modelId) {
m_modelId = modelId;
}
}
QString Light::type() const
{
return m_type;
}
void Light::setType(const QString &type)
{
if (m_type != type) {
m_type = type;
}
}
QString Light::swversion() const
{
return m_swversion;
}
void Light::setSwversion(const QString &swversion)
{
if (m_swversion != swversion) {
m_swversion = swversion;
}
}
bool Light::on() const
{
return m_on;
}
void Light::setOn(bool on)
{
if (m_on != on) {
QVariantMap params;
params.insert("on", on);
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
}
}
quint8 Light::bri() const
{
return m_bri;
}
void Light::setBri(quint8 bri)
{
if (m_bri != bri) {
qDebug() << "setting brightness to" << bri << m_busyStateChangeId;
if (m_busyStateChangeId == -1) {
QVariantMap params;
if (bri == 0) {
params.insert("bri", bri);
params.insert("on", false);
} else {
params.insert("bri", bri);
params.insert("on", true);
}
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else {
m_dirtyBri = bri;
m_briDirty = true;
}
}
}
quint16 Light::hue() const
{
return m_hue;
}
void Light::setHue(quint16 hue)
{
if (m_hue != hue) {
m_hue = hue;
emit stateChanged();
}
}
quint8 Light::sat() const
{
return m_sat;
}
void Light::setSat(quint8 sat)
{
if (m_sat != sat) {
m_sat = sat;
emit stateChanged();
}
}
QColor Light::color() const
{
return QColor::fromHsv(m_hue * 360 / 65535, m_sat, 255);
}
void Light::setColor(const QColor &color)
{
// Transform from RGB to Hue/Sat
quint16 hue = color.hue() * 65535 / 360;
quint8 sat = color.saturation();
qDebug() << "setting color" << color;
if (m_busyStateChangeId == -1) {
QVariantMap params;
params.insert("hue", hue);
params.insert("sat", sat);
// FIXME: There is a bug in the API that it doesn't report back the set state of "sat"
// Lets just assume it always succeeds
m_sat = sat;
params.insert("on", true);
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else {
m_dirtyHue = hue;
m_hueDirty = true;
m_dirtySat = sat;
m_satDirty = true;
}
}
QPointF Light::xy() const
{
return m_xy;
}
void Light::setXy(const QPointF &xy)
{
if (m_xy != xy) {
m_xy = xy;
emit stateChanged();
}
}
quint16 Light::ct() const
{
return m_ct;
}
void Light::setCt(quint16 ct)
{
if (m_busyStateChangeId == -1) {
QVariantMap params;
params.insert("ct", ct);
params.insert("on", true);
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else {
m_dirtyCt = ct;
m_ctDirty = true;
}
}
QString Light::alert() const
{
return m_alert;
}
void Light::setAlert(const QString &alert)
{
if (m_alert != alert) {
m_alert = alert;
emit stateChanged();
}
}
QString Light::effect() const
{
return m_effect;
}
void Light::setEffect(const QString &effect)
{
if (m_effect != effect) {
QVariantMap params;
params.insert("effect", effect);
if (effect != "none") {
params.insert("on", true);
}
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
}
}
Light::ColorMode Light::colorMode() const
{
return m_colormode;
}
bool Light::reachable() const
{
return m_reachable;
}
void Light::refresh()
{
m_bridge->get(m_ip, m_username, "lights/" + QString::number(m_id), this, "responseReceived");
}
void Light::setReachable(bool reachable)
{
if (m_reachable != reachable) {
m_reachable = reachable;
emit stateChanged();
}
}
void Light::responseReceived(int id, const QVariant &response)
{
Q_UNUSED(id)
QVariantMap attributes = response.toMap();
m_name = attributes.value("name").toString();
setModelId(attributes.value("modelid").toString());
setType(attributes.value("type").toString());
setSwversion(attributes.value("swversion").toString());
QVariantMap stateMap = attributes.value("state").toMap();
m_on = stateMap.value("on").toBool();
m_bri = stateMap.value("bri").toInt();
m_hue = stateMap.value("hue").toInt();
m_sat = stateMap.value("sat").toInt();
m_xy = stateMap.value("xy").toPointF();
m_ct = stateMap.value("ct").toInt();
m_alert = stateMap.value("alert").toString();
m_effect = stateMap.value("effect").toString();
QString colorModeString = stateMap.value("colormode").toString();
if (colorModeString == "hs") {
m_colormode = ColorModeHS;
} else if (colorModeString == "xy") {
m_colormode = ColorModeXY;
} else if (colorModeString == "ct") {
m_colormode = ColorModeCT;
}
m_reachable = stateMap.value("reachable").toBool();
emit stateChanged();
//qDebug() << "got light response" << m_modelId << m_type << m_swversion << m_on << m_bri << m_reachable;
}
void Light::setDescriptionFinished(int id, const QVariant &response)
{
Q_UNUSED(id)
QVariantMap result = response.toList().first().toMap();
if (result.contains("success")) {
QVariantMap successMap = result.value("success").toMap();
if (successMap.contains("/lights/" + QString::number(m_id) + "/name")) {
m_name = successMap.value("/lights/" + QString::number(m_id) + "/name").toString();
emit stateChanged();
}
}
}
void Light::setStateFinished(int id, const QVariant &response)
{
foreach (const QVariant &resultVariant, response.toList()) {
QVariantMap result = resultVariant.toMap();
if (result.contains("success")) {
QVariantMap successMap = result.value("success").toMap();
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/on")) {
m_on = successMap.value("/lights/" + QString::number(m_id) + "/state/on").toBool();
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/hue")) {
m_hue = successMap.value("/lights/" + QString::number(m_id) + "/state/hue").toInt();
m_colormode = ColorModeHS;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/bri")) {
m_bri = successMap.value("/lights/" + QString::number(m_id) + "/state/bri").toInt();
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/sat")) {
m_sat = successMap.value("/lights/" + QString::number(m_id) + "/state/sat").toInt();
m_colormode = ColorModeHS;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/xy")) {
m_xy = successMap.value("/lights/" + QString::number(m_id) + "/state/xy").toPoint();
m_colormode = ColorModeXY;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/ct")) {
m_ct = successMap.value("/lights/" + QString::number(m_id) + "/state/ct").toInt();
m_colormode = ColorModeCT;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/effect")) {
m_effect = successMap.value("/lights/" + QString::number(m_id) + "/state/effect").toString();
}
}
}
emit stateChanged();
if (m_busyStateChangeId == id) {
m_busyTimeout.stop();
m_busyStateChangeId = -1;
if (m_hueDirty || m_satDirty || m_briDirty) {
QVariantMap params;
if (m_hueDirty) {
params.insert("hue", m_dirtyHue);
m_hueDirty = false;
}
if (m_satDirty) {
params.insert("sat", m_dirtySat);
m_satDirty = false;
}
if (m_briDirty) {
params.insert("bri", m_dirtyBri);
m_briDirty = false;
}
// FIXME: There is a bug in the API that it doesn't report back the set state of "sat"
// Lets just assume it always succeeds
m_sat = m_dirtySat;
m_busyStateChangeId = m_bridge->put(QHostAddress(m_ip), m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else if(m_ctDirty) {
QVariantMap params;
params.insert("ct", m_dirtyCt);
m_ctDirty = false;
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else if (m_xyDirty) {
QVariantMap params;
QVariantList xyList;
xyList << m_dirtyXy.x() << m_dirtyXy.y();
params.insert("xy", xyList);
m_xyDirty = false;
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
}
}
}

View File

@ -6,14 +6,18 @@ QT += network
SOURCES += \ SOURCES += \
devicepluginphilipshue.cpp \ devicepluginphilipshue.cpp \
huebridgeconnection.cpp \ #huebridgeconnection.cpp \
light.cpp #light.cpp \
huebridge.cpp \
huelight.cpp
HEADERS += \ HEADERS += \
devicepluginphilipshue.h \ devicepluginphilipshue.h \
huebridgeconnection.h \ #huebridgeconnection.h \
light.h \ #light.h \
lightinterface.h #lightinterface.h \
huebridge.h \
huelight.h

View File

@ -719,6 +719,7 @@
"UnitRadiant", "UnitRadiant",
"UnitDegreeCelsius", "UnitDegreeCelsius",
"UnitDegreeKelvin", "UnitDegreeKelvin",
"UnitMired",
"UnitMilliBar", "UnitMilliBar",
"UnitBar", "UnitBar",
"UnitPascal", "UnitPascal",