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